]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 20 Oct 2016 15:49:03 +0000 (08:49 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 20 Oct 2016 15:49:03 +0000 (08:49 -0700)
Pull misc filesystem fixes from Jan Kara:
 "A fix for an isofs change apparently breaking mount(8) in some cases
  and one ext2 warning fix"

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  ext2: avoid bogus -Wmaybe-uninitialized warning
  isofs: Do not return EACCES for unknown filesystems

1456 files changed:
.mailmap
Documentation/00-INDEX
Documentation/80211/cfg80211.rst [new file with mode: 0644]
Documentation/80211/conf.py [new file with mode: 0644]
Documentation/80211/index.rst [new file with mode: 0644]
Documentation/80211/introduction.rst [new file with mode: 0644]
Documentation/80211/mac80211-advanced.rst [new file with mode: 0644]
Documentation/80211/mac80211.rst [new file with mode: 0644]
Documentation/DocBook/80211.tmpl [deleted file]
Documentation/DocBook/Makefile
Documentation/Makefile
Documentation/accounting/Makefile [deleted file]
Documentation/accounting/delay-accounting.txt
Documentation/arm/00-INDEX
Documentation/auxdisplay/Makefile [deleted file]
Documentation/auxdisplay/cfag12864b
Documentation/blackfin/00-INDEX
Documentation/blackfin/Makefile [deleted file]
Documentation/devicetree/bindings/auxdisplay/img-ascii-lcd.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/i2c.txt
Documentation/devicetree/bindings/i2c/trivial-devices.txt
Documentation/devicetree/bindings/infiniband/hisilicon-hns-roce.txt
Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mips/brcm/soc.txt
Documentation/devicetree/bindings/pinctrl/pinctrl-aspeed.txt
Documentation/devicetree/bindings/pwm/pwm-meson.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
Documentation/devicetree/bindings/pwm/pwm-st.txt
Documentation/devicetree/bindings/pwm/pwm-sun4i.txt
Documentation/devicetree/bindings/rtc/dallas,ds1390.txt
Documentation/devicetree/bindings/rtc/epson,rx8900.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/rtc-omap.txt
Documentation/devicetree/bindings/thermal/max77620_thermal.txt [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/mediatek-thermal.txt
Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt
Documentation/devicetree/bindings/thermal/qcom-tsens.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/devicetree/bindings/watchdog/of-xilinx-wdt.txt
Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt
Documentation/features/perf/kprobes-event/arch-support.txt
Documentation/filesystems/00-INDEX
Documentation/filesystems/Makefile [deleted file]
Documentation/ia64/Makefile [deleted file]
Documentation/index.rst
Documentation/input/alps.txt
Documentation/kbuild/makefiles.txt
Documentation/kernel-parameters.txt
Documentation/kselftest.txt
Documentation/laptops/00-INDEX
Documentation/laptops/Makefile [deleted file]
Documentation/laptops/laptop-mode.txt
Documentation/mic/Makefile [deleted file]
Documentation/mic/mpssd/Makefile [deleted file]
Documentation/misc-devices/Makefile [deleted file]
Documentation/misc-devices/mei/Makefile [deleted file]
Documentation/networking/00-INDEX
Documentation/networking/Makefile [deleted file]
Documentation/networking/timestamping/Makefile [deleted file]
Documentation/pcmcia/Makefile [deleted file]
Documentation/pcmcia/devicetable.txt
Documentation/prctl/Makefile [deleted file]
Documentation/ptp/Makefile [deleted file]
Documentation/scsi/g_NCR5380.txt
Documentation/spi/00-INDEX
Documentation/thermal/sysfs-api.txt
Documentation/timers/00-INDEX
Documentation/timers/Makefile [deleted file]
Documentation/timers/hpet.txt
Documentation/vDSO/Makefile [deleted file]
Documentation/watchdog/Makefile [deleted file]
Documentation/watchdog/src/Makefile [deleted file]
Documentation/watchdog/watchdog-api.txt
Documentation/watchdog/watchdog-kernel-api.txt
Documentation/watchdog/wdt.txt
MAINTAINERS
Makefile
arch/Kconfig
arch/alpha/include/asm/Kbuild
arch/alpha/include/asm/uaccess.h
arch/alpha/kernel/Makefile
arch/alpha/kernel/alpha_ksyms.c [deleted file]
arch/alpha/kernel/machvec_impl.h
arch/alpha/kernel/ptrace.c
arch/alpha/kernel/setup.c
arch/alpha/lib/callback_srm.S
arch/alpha/lib/checksum.c
arch/alpha/lib/clear_page.S
arch/alpha/lib/clear_user.S
arch/alpha/lib/copy_page.S
arch/alpha/lib/copy_user.S
arch/alpha/lib/csum_ipv6_magic.S
arch/alpha/lib/csum_partial_copy.c
arch/alpha/lib/dec_and_lock.c
arch/alpha/lib/divide.S
arch/alpha/lib/ev6-clear_page.S
arch/alpha/lib/ev6-clear_user.S
arch/alpha/lib/ev6-copy_page.S
arch/alpha/lib/ev6-copy_user.S
arch/alpha/lib/ev6-csum_ipv6_magic.S
arch/alpha/lib/ev6-divide.S
arch/alpha/lib/ev6-memchr.S
arch/alpha/lib/ev6-memcpy.S
arch/alpha/lib/ev6-memset.S
arch/alpha/lib/ev67-strcat.S
arch/alpha/lib/ev67-strchr.S
arch/alpha/lib/ev67-strlen.S
arch/alpha/lib/ev67-strncat.S
arch/alpha/lib/ev67-strrchr.S
arch/alpha/lib/fpreg.c
arch/alpha/lib/memchr.S
arch/alpha/lib/memcpy.c
arch/alpha/lib/memmove.S
arch/alpha/lib/memset.S
arch/alpha/lib/strcat.S
arch/alpha/lib/strchr.S
arch/alpha/lib/strcpy.S
arch/alpha/lib/strlen.S
arch/alpha/lib/strncat.S
arch/alpha/lib/strncpy.S
arch/alpha/lib/strrchr.S
arch/arc/kernel/signal.c
arch/arm/boot/dts/tegra124-jetson-tk1.dts
arch/arm/boot/dts/tegra124.dtsi
arch/arm/configs/exynos_defconfig
arch/arm/include/asm/Kbuild
arch/arm/include/asm/uaccess.h
arch/arm/kernel/Makefile
arch/arm/kernel/armksyms.c [deleted file]
arch/arm/kernel/entry-ftrace.S
arch/arm/kernel/head.S
arch/arm/kernel/smccc-call.S
arch/arm/lib/ashldi3.S
arch/arm/lib/ashrdi3.S
arch/arm/lib/bitops.h
arch/arm/lib/bswapsdi2.S
arch/arm/lib/clear_user.S
arch/arm/lib/copy_from_user.S
arch/arm/lib/copy_page.S
arch/arm/lib/copy_to_user.S
arch/arm/lib/csumipv6.S
arch/arm/lib/csumpartial.S
arch/arm/lib/csumpartialcopy.S
arch/arm/lib/csumpartialcopygeneric.S
arch/arm/lib/csumpartialcopyuser.S
arch/arm/lib/delay.c
arch/arm/lib/div64.S
arch/arm/lib/findbit.S
arch/arm/lib/getuser.S
arch/arm/lib/io-readsb.S
arch/arm/lib/io-readsl.S
arch/arm/lib/io-readsw-armv3.S
arch/arm/lib/io-readsw-armv4.S
arch/arm/lib/io-writesb.S
arch/arm/lib/io-writesl.S
arch/arm/lib/io-writesw-armv3.S
arch/arm/lib/io-writesw-armv4.S
arch/arm/lib/lib1funcs.S
arch/arm/lib/lshrdi3.S
arch/arm/lib/memchr.S
arch/arm/lib/memcpy.S
arch/arm/lib/memmove.S
arch/arm/lib/memset.S
arch/arm/lib/memzero.S
arch/arm/lib/muldi3.S
arch/arm/lib/putuser.S
arch/arm/lib/strchr.S
arch/arm/lib/strrchr.S
arch/arm/lib/uaccess_with_memcpy.c
arch/arm/lib/ucmpdi2.S
arch/arm/mach-imx/Makefile
arch/arm/mach-imx/ssi-fiq-ksym.c [deleted file]
arch/arm/mach-imx/ssi-fiq.S
arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
arch/arm64/boot/dts/nvidia/tegra132.dtsi
arch/arm64/boot/dts/nvidia/tegra210.dtsi
arch/arm64/include/asm/uaccess.h
arch/arm64/kernel/sleep.S
arch/arm64/lib/copy_from_user.S
arch/blackfin/include/asm/uaccess.h
arch/blackfin/kernel/ptrace.c
arch/cris/arch-v32/drivers/cryptocop.c
arch/cris/arch-v32/kernel/ptrace.c
arch/ia64/hp/sim/boot/Makefile
arch/ia64/include/asm/export.h [new file with mode: 0644]
arch/ia64/include/asm/libata-portmap.h
arch/ia64/kernel/entry.S
arch/ia64/kernel/err_inject.c
arch/ia64/kernel/esi_stub.S
arch/ia64/kernel/head.S
arch/ia64/kernel/ia64_ksyms.c
arch/ia64/kernel/ivt.S
arch/ia64/kernel/pal.S
arch/ia64/kernel/ptrace.c
arch/ia64/kernel/setup.c
arch/ia64/lib/Makefile
arch/ia64/lib/clear_page.S
arch/ia64/lib/clear_user.S
arch/ia64/lib/copy_page.S
arch/ia64/lib/copy_page_mck.S
arch/ia64/lib/copy_user.S
arch/ia64/lib/flush.S
arch/ia64/lib/idiv32.S
arch/ia64/lib/idiv64.S
arch/ia64/lib/ip_fast_csum.S
arch/ia64/lib/memcpy.S
arch/ia64/lib/memcpy_mck.S
arch/ia64/lib/memset.S
arch/ia64/lib/strlen.S
arch/ia64/lib/strlen_user.S
arch/ia64/lib/strncpy_from_user.S
arch/ia64/lib/strnlen_user.S
arch/ia64/lib/xor.S
arch/m32r/kernel/ptrace.c
arch/m68k/include/asm/export.h [new file with mode: 0644]
arch/m68k/kernel/Makefile
arch/m68k/kernel/m68k_ksyms.c [deleted file]
arch/m68k/lib/ashldi3.c
arch/m68k/lib/ashrdi3.c
arch/m68k/lib/divsi3.S
arch/m68k/lib/lshrdi3.c
arch/m68k/lib/modsi3.S
arch/m68k/lib/muldi3.c
arch/m68k/lib/mulsi3.S
arch/m68k/lib/udivsi3.S
arch/m68k/lib/umodsi3.S
arch/metag/include/asm/atomic.h
arch/mips/Kbuild.platforms
arch/mips/Kconfig
arch/mips/Makefile
arch/mips/alchemy/common/setup.c
arch/mips/bcm47xx/serial.c
arch/mips/bcm63xx/clk.c
arch/mips/bmips/Kconfig
arch/mips/bmips/setup.c
arch/mips/boot/Makefile
arch/mips/boot/dts/brcm/Makefile
arch/mips/boot/dts/brcm/bcm3368-netgear-cvg834g.dts [new file with mode: 0644]
arch/mips/boot/dts/brcm/bcm3368.dtsi [new file with mode: 0644]
arch/mips/boot/dts/brcm/bcm63268-comtrend-vr-3032u.dts [new file with mode: 0644]
arch/mips/boot/dts/brcm/bcm63268.dtsi [new file with mode: 0644]
arch/mips/boot/dts/brcm/bcm6358-neufbox4-sercomm.dts [moved from arch/mips/boot/dts/brcm/bcm96358nb4ser.dts with 94% similarity]
arch/mips/boot/dts/brcm/bcm6362-neufbox6-sercomm.dts [new file with mode: 0644]
arch/mips/boot/dts/brcm/bcm6362.dtsi [new file with mode: 0644]
arch/mips/boot/dts/brcm/bcm7125.dtsi
arch/mips/boot/dts/brcm/bcm7346.dtsi
arch/mips/boot/dts/brcm/bcm7358.dtsi
arch/mips/boot/dts/brcm/bcm7360.dtsi
arch/mips/boot/dts/brcm/bcm7362.dtsi
arch/mips/boot/dts/brcm/bcm7420.dtsi
arch/mips/boot/dts/brcm/bcm7425.dtsi
arch/mips/boot/dts/brcm/bcm7435.dtsi
arch/mips/boot/dts/brcm/bcm97125cbmb.dts
arch/mips/boot/dts/brcm/bcm97346dbsmb.dts
arch/mips/boot/dts/brcm/bcm97358svmb.dts
arch/mips/boot/dts/brcm/bcm97360svmb.dts
arch/mips/boot/dts/brcm/bcm97362svmb.dts
arch/mips/boot/dts/brcm/bcm97420c.dts
arch/mips/boot/dts/brcm/bcm97425svmb.dts
arch/mips/boot/dts/brcm/bcm97435svmb.dts
arch/mips/boot/dts/brcm/bcm97xxx-nand-cs1-bch24.dtsi [new file with mode: 0644]
arch/mips/boot/dts/brcm/bcm97xxx-nand-cs1-bch4.dtsi [new file with mode: 0644]
arch/mips/boot/dts/cavium-octeon/dlink_dsr-1000n.dts
arch/mips/boot/dts/cavium-octeon/dlink_dsr-500n-1000n.dtsi [new file with mode: 0644]
arch/mips/boot/dts/cavium-octeon/dlink_dsr-500n.dts [new file with mode: 0644]
arch/mips/boot/dts/mti/Makefile
arch/mips/boot/dts/mti/malta.dts
arch/mips/boot/dts/mti/sead3.dts
arch/mips/cavium-octeon/executive/cvmx-helper-board.c
arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c
arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c
arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c
arch/mips/cavium-octeon/executive/cvmx-helper.c
arch/mips/cavium-octeon/setup.c
arch/mips/configs/generic/32r1.config [new file with mode: 0644]
arch/mips/configs/generic/32r2.config [new file with mode: 0644]
arch/mips/configs/generic/32r6.config [new file with mode: 0644]
arch/mips/configs/generic/64r1.config [new file with mode: 0644]
arch/mips/configs/generic/64r2.config [new file with mode: 0644]
arch/mips/configs/generic/64r6.config [new file with mode: 0644]
arch/mips/configs/generic/board-sead-3.config [new file with mode: 0644]
arch/mips/configs/generic/eb.config [new file with mode: 0644]
arch/mips/configs/generic/el.config [new file with mode: 0644]
arch/mips/configs/generic/micro32r2.config [new file with mode: 0644]
arch/mips/configs/generic_defconfig [new file with mode: 0644]
arch/mips/configs/loongson1c_defconfig [new file with mode: 0644]
arch/mips/configs/malta_defconfig
arch/mips/configs/malta_kvm_defconfig
arch/mips/configs/malta_kvm_guest_defconfig
arch/mips/configs/malta_qemu_32r6_defconfig
arch/mips/configs/maltaaprp_defconfig
arch/mips/configs/maltasmvp_defconfig
arch/mips/configs/maltasmvp_eva_defconfig
arch/mips/configs/maltaup_defconfig
arch/mips/configs/maltaup_xpa_defconfig
arch/mips/configs/pistachio_defconfig
arch/mips/configs/sead3_defconfig [deleted file]
arch/mips/configs/sead3micro_defconfig [deleted file]
arch/mips/generic/Kconfig [new file with mode: 0644]
arch/mips/generic/Makefile [new file with mode: 0644]
arch/mips/generic/Platform [new file with mode: 0644]
arch/mips/generic/board-sead3.c [new file with mode: 0644]
arch/mips/generic/init.c [new file with mode: 0644]
arch/mips/generic/irq.c [new file with mode: 0644]
arch/mips/generic/proc.c [new file with mode: 0644]
arch/mips/generic/vmlinux.its.S [new file with mode: 0644]
arch/mips/include/asm/addrspace.h
arch/mips/include/asm/barrier.h
arch/mips/include/asm/cacheflush.h
arch/mips/include/asm/cpu-type.h
arch/mips/include/asm/cpu.h
arch/mips/include/asm/device.h
arch/mips/include/asm/dma-coherence.h
arch/mips/include/asm/dma-mapping.h
arch/mips/include/asm/i8259.h
arch/mips/include/asm/mach-generic/dma-coherence.h
arch/mips/include/asm/mach-generic/floppy.h
arch/mips/include/asm/mach-generic/spaces.h
arch/mips/include/asm/mach-ip27/spaces.h
arch/mips/include/asm/mach-loongson32/irq.h
arch/mips/include/asm/mach-loongson32/loongson1.h
arch/mips/include/asm/mach-loongson32/platform.h
arch/mips/include/asm/mach-loongson32/regs-clk.h
arch/mips/include/asm/mach-loongson32/regs-mux.h
arch/mips/include/asm/mach-sead3/cpu-feature-overrides.h [deleted file]
arch/mips/include/asm/mach-sead3/irq.h [deleted file]
arch/mips/include/asm/mach-sead3/kernel-entry-init.h [deleted file]
arch/mips/include/asm/mach-sead3/war.h [deleted file]
arch/mips/include/asm/machine.h [new file with mode: 0644]
arch/mips/include/asm/mips-boards/sead3int.h [deleted file]
arch/mips/include/asm/mips-cm.h
arch/mips/include/asm/octeon/cvmx-helper-board.h
arch/mips/include/asm/octeon/cvmx-mdio.h [deleted file]
arch/mips/include/asm/pci.h
arch/mips/include/asm/pgalloc.h
arch/mips/include/asm/pm-cps.h
arch/mips/include/asm/ptrace.h
arch/mips/include/asm/smp.h
arch/mips/include/asm/uaccess.h
arch/mips/include/uapi/asm/unistd.h
arch/mips/kernel/binfmt_elfn32.c
arch/mips/kernel/binfmt_elfo32.c
arch/mips/kernel/branch.c
arch/mips/kernel/kprobes.c
arch/mips/kernel/linux32.c
arch/mips/kernel/mips-cpc.c
arch/mips/kernel/mips-r2-to-r6-emul.c
arch/mips/kernel/module.c
arch/mips/kernel/pm-cps.c
arch/mips/kernel/probes-common.h [new file with mode: 0644]
arch/mips/kernel/proc.c
arch/mips/kernel/ptrace32.c
arch/mips/kernel/scall32-o32.S
arch/mips/kernel/scall64-64.S
arch/mips/kernel/scall64-n32.S
arch/mips/kernel/scall64-o32.S
arch/mips/kernel/smp-gic.c [deleted file]
arch/mips/kernel/smp-mt.c
arch/mips/kernel/smp.c
arch/mips/kernel/traps.c
arch/mips/kernel/uprobes.c
arch/mips/kvm/commpage.c
arch/mips/kvm/dyntrans.c
arch/mips/kvm/emulate.c
arch/mips/kvm/interrupt.c
arch/mips/kvm/trap_emul.c
arch/mips/lantiq/xway/vmmc.c
arch/mips/lantiq/xway/xrx200_phy_fw.c
arch/mips/lib/ashldi3.c
arch/mips/lib/ashrdi3.c
arch/mips/lib/bswapdi.c
arch/mips/lib/bswapsi.c
arch/mips/lib/cmpdi2.c
arch/mips/lib/delay.c
arch/mips/lib/iomap-pci.c
arch/mips/lib/iomap.c
arch/mips/lib/lshrdi3.c
arch/mips/lib/ucmpdi2.c
arch/mips/loongson32/Kconfig
arch/mips/loongson32/Makefile
arch/mips/loongson32/Platform
arch/mips/loongson32/common/irq.c
arch/mips/loongson32/common/platform.c
arch/mips/loongson32/common/setup.c
arch/mips/loongson32/ls1c/Makefile [new file with mode: 0644]
arch/mips/loongson32/ls1c/board.c [new file with mode: 0644]
arch/mips/mm/c-octeon.c
arch/mips/mm/c-r3k.c
arch/mips/mm/c-r4k.c
arch/mips/mm/c-tx39.c
arch/mips/mm/cache.c
arch/mips/mm/dma-default.c
arch/mips/mm/extable.c
arch/mips/mm/fault.c
arch/mips/mm/gup.c
arch/mips/mm/highmem.c
arch/mips/mm/init.c
arch/mips/mm/ioremap.c
arch/mips/mm/mmap.c
arch/mips/mm/page.c
arch/mips/mm/tlb-r4k.c
arch/mips/mti-malta/malta-dt.c
arch/mips/mti-malta/malta-dtshim.c
arch/mips/mti-malta/malta-init.c
arch/mips/mti-malta/malta-int.c
arch/mips/mti-malta/malta-platform.c
arch/mips/mti-malta/malta-reset.c
arch/mips/mti-malta/malta-setup.c
arch/mips/mti-sead3/Makefile [deleted file]
arch/mips/mti-sead3/Platform [deleted file]
arch/mips/mti-sead3/sead3-console.c [deleted file]
arch/mips/mti-sead3/sead3-display.c [deleted file]
arch/mips/mti-sead3/sead3-init.c [deleted file]
arch/mips/mti-sead3/sead3-int.c [deleted file]
arch/mips/mti-sead3/sead3-lcd.c [deleted file]
arch/mips/mti-sead3/sead3-platform.c [deleted file]
arch/mips/mti-sead3/sead3-reset.c [deleted file]
arch/mips/mti-sead3/sead3-setup.c [deleted file]
arch/mips/mti-sead3/sead3-time.c [deleted file]
arch/mips/pci/Makefile
arch/mips/pci/pci-alchemy.c
arch/mips/pci/pci-ar71xx.c
arch/mips/pci/pci-ar724x.c
arch/mips/pci/pci-generic.c [new file with mode: 0644]
arch/mips/pci/pci-lantiq.c
arch/mips/pci/pci-legacy.c [new file with mode: 0644]
arch/mips/pci/pci-mt7620.c
arch/mips/pci/pci-octeon.c
arch/mips/pci/pci-rt2880.c
arch/mips/pci/pci-rt3883.c
arch/mips/pci/pci.c
arch/mips/pci/pcie-octeon.c
arch/mips/pnx833x/common/platform.c
arch/mips/ralink/timer.c
arch/mips/txx9/Kconfig
arch/mips/txx9/generic/pci.c
arch/mips/txx9/generic/setup.c
arch/mips/txx9/generic/setup_tx3927.c
arch/mips/txx9/generic/setup_tx4927.c
arch/mips/txx9/generic/setup_tx4938.c
arch/mips/txx9/jmr3927/setup.c
arch/mips/txx9/rbtx4927/setup.c
arch/mips/txx9/rbtx4938/setup.c
arch/mips/vdso/Makefile
arch/powerpc/Makefile
arch/powerpc/configs/dpaa.config [new file with mode: 0644]
arch/powerpc/include/asm/Kbuild
arch/powerpc/include/asm/cputable.h
arch/powerpc/include/asm/hw_irq.h
arch/powerpc/include/asm/libata-portmap.h
arch/powerpc/include/asm/ppc-opcode.h
arch/powerpc/include/asm/reg.h
arch/powerpc/include/asm/reg_8xx.h
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/cputable.c
arch/powerpc/kernel/entry_32.S
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/epapr_hcalls.S
arch/powerpc/kernel/exceptions-64s.S
arch/powerpc/kernel/fpu.S
arch/powerpc/kernel/head_32.S
arch/powerpc/kernel/head_40x.S
arch/powerpc/kernel/head_44x.S
arch/powerpc/kernel/head_64.S
arch/powerpc/kernel/head_8xx.S
arch/powerpc/kernel/head_fsl_booke.S
arch/powerpc/kernel/misc.S
arch/powerpc/kernel/misc_32.S
arch/powerpc/kernel/misc_64.S
arch/powerpc/kernel/pci-common.c
arch/powerpc/kernel/pci_32.c
arch/powerpc/kernel/ppc_ksyms.c [deleted file]
arch/powerpc/kernel/ppc_ksyms_32.c [deleted file]
arch/powerpc/kernel/ptrace32.c
arch/powerpc/kernel/setup-common.c
arch/powerpc/kernel/setup_32.c
arch/powerpc/kernel/time.c
arch/powerpc/kernel/traps.c
arch/powerpc/kernel/vector.S
arch/powerpc/lib/Makefile
arch/powerpc/lib/checksum_32.S
arch/powerpc/lib/checksum_64.S
arch/powerpc/lib/copy_32.S
arch/powerpc/lib/copypage_64.S
arch/powerpc/lib/copyuser_64.S
arch/powerpc/lib/hweight_64.S
arch/powerpc/lib/mem_64.S
arch/powerpc/lib/memcmp_64.S
arch/powerpc/lib/memcpy_64.S
arch/powerpc/lib/ppc_ksyms.c [deleted file]
arch/powerpc/lib/string.S
arch/powerpc/lib/string_64.S
arch/powerpc/mm/hash_low_32.S
arch/powerpc/mm/hash_utils_64.c
arch/powerpc/platforms/82xx/Kconfig
arch/powerpc/platforms/82xx/ep8248e.c
arch/powerpc/platforms/83xx/asp834x.c
arch/powerpc/platforms/83xx/km83xx.c
arch/powerpc/platforms/83xx/misc.c
arch/powerpc/platforms/83xx/mpc830x_rdb.c
arch/powerpc/platforms/83xx/mpc831x_rdb.c
arch/powerpc/platforms/83xx/mpc832x_mds.c
arch/powerpc/platforms/83xx/mpc832x_rdb.c
arch/powerpc/platforms/83xx/mpc834x_itx.c
arch/powerpc/platforms/83xx/mpc834x_mds.c
arch/powerpc/platforms/83xx/mpc836x_mds.c
arch/powerpc/platforms/83xx/mpc836x_rdk.c
arch/powerpc/platforms/83xx/mpc837x_mds.c
arch/powerpc/platforms/83xx/mpc837x_rdb.c
arch/powerpc/platforms/83xx/mpc83xx.h
arch/powerpc/platforms/83xx/sbc834x.c
arch/powerpc/platforms/85xx/Kconfig
arch/powerpc/platforms/85xx/bsc913x_qds.c
arch/powerpc/platforms/85xx/bsc913x_rdb.c
arch/powerpc/platforms/85xx/c293pcie.c
arch/powerpc/platforms/85xx/corenet_generic.c
arch/powerpc/platforms/85xx/ge_imp3a.c
arch/powerpc/platforms/85xx/mpc8536_ds.c
arch/powerpc/platforms/85xx/mpc85xx_ads.c
arch/powerpc/platforms/85xx/mpc85xx_cds.c
arch/powerpc/platforms/85xx/mpc85xx_ds.c
arch/powerpc/platforms/85xx/mpc85xx_mds.c
arch/powerpc/platforms/85xx/mpc85xx_rdb.c
arch/powerpc/platforms/85xx/mvme2500.c
arch/powerpc/platforms/85xx/p1010rdb.c
arch/powerpc/platforms/85xx/p1022_ds.c
arch/powerpc/platforms/85xx/p1022_rdk.c
arch/powerpc/platforms/85xx/p1023_rdb.c
arch/powerpc/platforms/85xx/ppa8548.c
arch/powerpc/platforms/85xx/qemu_e500.c
arch/powerpc/platforms/85xx/sbc8548.c
arch/powerpc/platforms/85xx/sgy_cts1000.c
arch/powerpc/platforms/85xx/socrates.c
arch/powerpc/platforms/85xx/stx_gp3.c
arch/powerpc/platforms/85xx/tqm85xx.c
arch/powerpc/platforms/85xx/twr_p102x.c
arch/powerpc/platforms/85xx/xes_mpc85xx.c
arch/powerpc/platforms/86xx/gef_ppc9a.c
arch/powerpc/platforms/86xx/gef_sbc310.c
arch/powerpc/platforms/86xx/gef_sbc610.c
arch/powerpc/platforms/86xx/mpc8610_hpcd.c
arch/powerpc/platforms/86xx/mpc86xx_hpcn.c
arch/powerpc/platforms/86xx/mvme7100.c
arch/powerpc/platforms/86xx/sbc8641d.c
arch/powerpc/platforms/pseries/lpar.c
arch/powerpc/relocs_check.sh
arch/powerpc/sysdev/cpm1.c
arch/powerpc/sysdev/cpm2.c
arch/powerpc/sysdev/cpm_common.c
arch/powerpc/sysdev/dcr-low.S
arch/powerpc/sysdev/fsl_pci.c
arch/powerpc/sysdev/fsl_soc.c
arch/powerpc/sysdev/fsl_soc.h
arch/powerpc/sysdev/mpic.c
arch/s390/include/asm/Kbuild
arch/s390/kernel/Makefile
arch/s390/kernel/entry.S
arch/s390/kernel/mcount.S
arch/s390/kernel/s390_ksyms.c [deleted file]
arch/s390/lib/mem.S
arch/s390/mm/gup.c
arch/score/kernel/ptrace.c
arch/score/kernel/traps.c
arch/sh/Makefile
arch/sh/boards/Kconfig
arch/sh/configs/j2_defconfig
arch/sh/mm/gup.c
arch/sparc/include/asm/Kbuild
arch/sparc/include/asm/string.h
arch/sparc/include/asm/string_32.h
arch/sparc/include/asm/string_64.h
arch/sparc/kernel/Makefile
arch/sparc/kernel/entry.S
arch/sparc/kernel/head_32.S
arch/sparc/kernel/head_64.S
arch/sparc/kernel/helpers.S
arch/sparc/kernel/hvcalls.S
arch/sparc/kernel/ptrace_64.c
arch/sparc/kernel/sparc_ksyms.c [new file with mode: 0644]
arch/sparc/kernel/sparc_ksyms_32.c [deleted file]
arch/sparc/kernel/sparc_ksyms_64.c [deleted file]
arch/sparc/lib/Makefile
arch/sparc/lib/U1memcpy.S
arch/sparc/lib/VISsave.S
arch/sparc/lib/ashldi3.S
arch/sparc/lib/ashrdi3.S
arch/sparc/lib/atomic_64.S
arch/sparc/lib/bitops.S
arch/sparc/lib/blockops.S
arch/sparc/lib/bzero.S
arch/sparc/lib/checksum_32.S
arch/sparc/lib/checksum_64.S
arch/sparc/lib/clear_page.S
arch/sparc/lib/copy_in_user.S
arch/sparc/lib/copy_page.S
arch/sparc/lib/copy_user.S
arch/sparc/lib/csum_copy.S
arch/sparc/lib/divdi3.S
arch/sparc/lib/ffs.S
arch/sparc/lib/hweight.S
arch/sparc/lib/ipcsum.S
arch/sparc/lib/ksyms.c [deleted file]
arch/sparc/lib/locks.S
arch/sparc/lib/lshrdi3.S
arch/sparc/lib/mcount.S
arch/sparc/lib/memcmp.S
arch/sparc/lib/memcpy.S
arch/sparc/lib/memmove.S
arch/sparc/lib/memscan_32.S
arch/sparc/lib/memscan_64.S
arch/sparc/lib/memset.S
arch/sparc/lib/muldi3.S
arch/sparc/lib/strlen.S
arch/sparc/lib/strncmp_32.S
arch/sparc/lib/strncmp_64.S
arch/sparc/lib/xor.S
arch/sparc/mm/gup.c
arch/x86/entry/entry_32.S
arch/x86/entry/entry_64.S
arch/x86/entry/syscalls/syscall_32.tbl
arch/x86/entry/syscalls/syscall_64.tbl
arch/x86/entry/thunk_32.S
arch/x86/entry/thunk_64.S
arch/x86/events/intel/core.c
arch/x86/events/intel/lbr.c
arch/x86/events/intel/rapl.c
arch/x86/events/intel/uncore.c
arch/x86/include/asm/export.h [new file with mode: 0644]
arch/x86/include/asm/intel-family.h
arch/x86/include/asm/msr-index.h
arch/x86/include/asm/percpu.h
arch/x86/include/asm/rwsem.h
arch/x86/kernel/Makefile
arch/x86/kernel/e820.c
arch/x86/kernel/head_32.S
arch/x86/kernel/head_64.S
arch/x86/kernel/i386_ksyms_32.c [deleted file]
arch/x86/kernel/kprobes/core.c
arch/x86/kernel/mcount_64.S
arch/x86/kernel/smp.c
arch/x86/kernel/step.c
arch/x86/kernel/x8664_ksyms_64.c [deleted file]
arch/x86/lib/checksum_32.S
arch/x86/lib/clear_page_64.S
arch/x86/lib/cmpxchg8b_emu.S
arch/x86/lib/copy_page_64.S
arch/x86/lib/copy_user_64.S
arch/x86/lib/csum-partial_64.c
arch/x86/lib/getuser.S
arch/x86/lib/hweight.S
arch/x86/lib/memcpy_64.S
arch/x86/lib/memmove_64.S
arch/x86/lib/memset_64.S
arch/x86/lib/putuser.S
arch/x86/lib/strstr_32.c
arch/x86/mm/gup.c
arch/x86/mm/mpx.c
arch/x86/um/Makefile
arch/x86/um/checksum_32.S
arch/x86/um/ksyms.c [deleted file]
arch/x86/um/ptrace_32.c
arch/x86/um/ptrace_64.c
block/blk-cgroup.c
block/blk-softirq.c
drivers/acpi/acpi_pad.c
drivers/acpi/ec.c
drivers/acpi/fan.c
drivers/acpi/osl.c
drivers/acpi/property.c
drivers/acpi/thermal.c
drivers/ata/ahci.c
drivers/ata/ahci.h
drivers/ata/ahci_qoriq.c
drivers/ata/ahci_st.c
drivers/ata/libahci.c
drivers/ata/libata-scsi.c
drivers/ata/pata_at91.c
drivers/ata/pata_octeon_cf.c
drivers/ata/sata_mv.c
drivers/auxdisplay/Kconfig
drivers/auxdisplay/Makefile
drivers/auxdisplay/img-ascii-lcd.c [new file with mode: 0644]
drivers/char/random.c
drivers/cpufreq/cppc_cpufreq.c
drivers/cpufreq/cpufreq_conservative.c
drivers/cpufreq/intel_pstate.c
drivers/cpuidle/Kconfig.mips
drivers/cpuidle/cpuidle-cps.c
drivers/devfreq/devfreq.c
drivers/devfreq/event/Kconfig
drivers/devfreq/event/exynos-nocp.c
drivers/firewire/nosy.c
drivers/gpio/gpio-pca953x.c
drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
drivers/gpu/drm/amd/amdgpu/cz_dpm.c
drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c
drivers/gpu/drm/amd/amdgpu/si_dpm.c
drivers/gpu/drm/amd/amdgpu/tonga_ih.c
drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c
drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
drivers/gpu/drm/amd/include/amd_shared.h
drivers/gpu/drm/amd/powerplay/eventmgr/eventactionchains.c
drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c
drivers/gpu/drm/drm_info.c
drivers/gpu/drm/etnaviv/etnaviv_gem.c
drivers/gpu/drm/exynos/exynos_drm_g2d.c
drivers/gpu/drm/i915/i915_gem_userptr.c
drivers/gpu/drm/radeon/r600_dpm.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_i2c.c
drivers/gpu/drm/radeon/radeon_ttm.c
drivers/gpu/drm/radeon/si.c
drivers/gpu/drm/radeon/sid.h
drivers/gpu/drm/via/via_dmablit.c
drivers/hid/hid-dr.c
drivers/hid/hid-ids.h
drivers/hid/hid-led.c
drivers/hid/usbhid/hid-quirks.c
drivers/i2c/i2c-core.c
drivers/infiniband/Kconfig
drivers/infiniband/core/umem.c
drivers/infiniband/core/umem_odp.c
drivers/infiniband/hw/Makefile
drivers/infiniband/hw/hns/hns_roce_cq.c
drivers/infiniband/hw/hns/hns_roce_device.h
drivers/infiniband/hw/hns/hns_roce_eq.c
drivers/infiniband/hw/hns/hns_roce_eq.h
drivers/infiniband/hw/hns/hns_roce_hem.c
drivers/infiniband/hw/hns/hns_roce_hem.h
drivers/infiniband/hw/hns/hns_roce_hw_v1.c
drivers/infiniband/hw/hns/hns_roce_hw_v1.h
drivers/infiniband/hw/hns/hns_roce_main.c
drivers/infiniband/hw/hns/hns_roce_mr.c
drivers/infiniband/hw/hns/hns_roce_pd.c
drivers/infiniband/hw/hns/hns_roce_qp.c
drivers/infiniband/hw/mthca/mthca_memfree.c
drivers/infiniband/hw/qedr/Kconfig [new file with mode: 0644]
drivers/infiniband/hw/qedr/Makefile [new file with mode: 0644]
drivers/infiniband/hw/qedr/main.c [new file with mode: 0644]
drivers/infiniband/hw/qedr/qedr.h [new file with mode: 0644]
drivers/infiniband/hw/qedr/qedr_cm.c [new file with mode: 0644]
drivers/infiniband/hw/qedr/qedr_cm.h [new file with mode: 0644]
drivers/infiniband/hw/qedr/qedr_hsi.h [new file with mode: 0644]
drivers/infiniband/hw/qedr/qedr_hsi_rdma.h [new file with mode: 0644]
drivers/infiniband/hw/qedr/verbs.c [new file with mode: 0644]
drivers/infiniband/hw/qedr/verbs.h [new file with mode: 0644]
drivers/infiniband/hw/qib/qib_user_pages.c
drivers/infiniband/hw/usnic/usnic_uiom.c
drivers/input/mouse/alps.c
drivers/input/mouse/alps.h
drivers/input/mouse/elantech.c
drivers/input/rmi4/rmi_i2c.c
drivers/input/rmi4/rmi_spi.c
drivers/input/serio/i8042-io.h
drivers/input/serio/i8042-ip22io.h
drivers/input/serio/i8042-ppcio.h
drivers/input/serio/i8042-sparcio.h
drivers/input/serio/i8042-unicore32io.h
drivers/input/serio/i8042-x86ia64io.h
drivers/input/serio/i8042.c
drivers/input/touchscreen/melfas_mip4.c
drivers/irqchip/irq-eznps.c
drivers/irqchip/irq-gic-v3.c
drivers/irqchip/irq-i8259.c
drivers/irqchip/irq-jcore-aic.c
drivers/media/pci/ivtv/ivtv-udma.c
drivers/media/pci/ivtv/ivtv-yuv.c
drivers/media/platform/omap/omap_vout.c
drivers/media/v4l2-core/Kconfig
drivers/media/v4l2-core/videobuf-dma-sg.c
drivers/media/v4l2-core/videobuf2-memops.c
drivers/misc/mic/scif/scif_rma.c
drivers/misc/sgi-gru/grufault.c
drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c
drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h
drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
drivers/net/ethernet/hisilicon/hns/hns_enet.c
drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/qlogic/Kconfig
drivers/net/ethernet/qlogic/qed/qed_ll2.c
drivers/net/ethernet/qlogic/qed/qed_roce.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
drivers/net/ethernet/ti/tlan.c
drivers/net/ethernet/xilinx/xilinx_axienet_main.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/phy/phy.c
drivers/net/usb/qmi_wwan.c
drivers/net/xen-netback/common.h
drivers/net/xen-netback/hash.c
drivers/net/xen-netback/rx.c
drivers/net/xen-netback/xenbus.c
drivers/of/platform.c
drivers/pci/host/pci-aardvark.c
drivers/pci/host/pci-dra7xx.c
drivers/pci/host/pci-exynos.c
drivers/pci/host/pci-imx6.c
drivers/pci/host/pci-keystone-dw.c
drivers/pci/host/pci-keystone.c
drivers/pci/host/pci-keystone.h
drivers/pci/host/pci-layerscape.c
drivers/pci/host/pci-mvebu.c
drivers/pci/host/pci-rcar-gen2.c
drivers/pci/host/pci-tegra.c
drivers/pci/host/pci-xgene.c
drivers/pci/host/pcie-altera.c
drivers/pci/host/pcie-armada8k.c
drivers/pci/host/pcie-artpec6.c
drivers/pci/host/pcie-designware-plat.c
drivers/pci/host/pcie-designware.c
drivers/pci/host/pcie-designware.h
drivers/pci/host/pcie-hisi.c
drivers/pci/host/pcie-iproc-bcma.c
drivers/pci/host/pcie-iproc-platform.c
drivers/pci/host/pcie-iproc.c
drivers/pci/host/pcie-qcom.c
drivers/pci/host/pcie-rcar.c
drivers/pci/host/pcie-rockchip.c
drivers/pci/host/pcie-spear13xx.c
drivers/pci/host/pcie-xilinx-nwl.c
drivers/pci/host/pcie-xilinx.c
drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
drivers/pinctrl/aspeed/pinctrl-aspeed.c
drivers/pinctrl/intel/pinctrl-baytrail.c
drivers/pinctrl/intel/pinctrl-intel.c
drivers/platform/goldfish/goldfish_pipe.c
drivers/platform/x86/Kconfig
drivers/platform/x86/acerhdf.c
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/asus-wmi.h
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel_pmc_core.c
drivers/platform/x86/intel_pmc_ipc.c
drivers/platform/x86/toshiba_acpi.c
drivers/platform/x86/toshiba_bluetooth.c
drivers/platform/x86/toshiba_haps.c
drivers/ptp/ptp_chardev.c
drivers/pwm/Kconfig
drivers/pwm/Makefile
drivers/pwm/core.c
drivers/pwm/pwm-berlin.c
drivers/pwm/pwm-cros-ec.c
drivers/pwm/pwm-lpc18xx-sct.c
drivers/pwm/pwm-meson.c [new file with mode: 0644]
drivers/pwm/pwm-mtk-disp.c
drivers/pwm/pwm-samsung.c
drivers/pwm/pwm-sti.c
drivers/pwm/pwm-sun4i.c
drivers/pwm/pwm-tipwmss.c
drivers/pwm/pwm-twl.c
drivers/pwm/sysfs.c
drivers/rapidio/devices/rio_mport_cdev.c
drivers/regulator/max8973-regulator.c
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/rtc-ac100.c
drivers/rtc/rtc-asm9260.c
drivers/rtc/rtc-at32ap700x.c
drivers/rtc/rtc-bq32k.c
drivers/rtc/rtc-cmos.c
drivers/rtc/rtc-coh901331.c
drivers/rtc/rtc-davinci.c
drivers/rtc/rtc-digicolor.c
drivers/rtc/rtc-ds1302.c
drivers/rtc/rtc-ds1307.c
drivers/rtc/rtc-ds1347.c
drivers/rtc/rtc-gemini.c
drivers/rtc/rtc-isl12057.c [deleted file]
drivers/rtc/rtc-jz4740.c
drivers/rtc/rtc-mcp795.c
drivers/rtc/rtc-mt6397.c
drivers/rtc/rtc-nuc900.c
drivers/rtc/rtc-omap.c
drivers/rtc/rtc-palmas.c
drivers/rtc/rtc-pcf2123.c
drivers/rtc/rtc-pcf50633.c
drivers/rtc/rtc-pic32.c
drivers/rtc/rtc-rv8803.c
drivers/rtc/rtc-rx6110.c
drivers/rtc/rtc-rx8025.c
drivers/rtc/rtc-spear.c
drivers/rtc/rtc-stmp3xxx.c
drivers/rtc/rtc-sysfs.c
drivers/rtc/rtc-tegra.c
drivers/rtc/rtc-twl.c
drivers/scsi/be2iscsi/be_main.c
drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
drivers/scsi/g_NCR5380.c
drivers/scsi/g_NCR5380.h
drivers/scsi/st.c
drivers/scsi/ufs/Kconfig
drivers/scsi/ufs/ufs_quirks.h
drivers/scsi/ufs/ufshcd.c
drivers/soc/Kconfig
drivers/soc/fsl/Makefile
drivers/soc/fsl/qbman/Kconfig [new file with mode: 0644]
drivers/soc/fsl/qbman/Makefile [new file with mode: 0644]
drivers/soc/fsl/qbman/bman.c [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_ccsr.c [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_portal.c [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_priv.h [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_test.c [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_test.h [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_test_api.c [new file with mode: 0644]
drivers/soc/fsl/qbman/dpaa_sys.h [new file with mode: 0644]
drivers/soc/fsl/qbman/qman.c [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_ccsr.c [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_portal.c [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_priv.h [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_test.c [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_test.h [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_test_api.c [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_test_stash.c [new file with mode: 0644]
drivers/soc/fsl/qe/gpio.c
drivers/soc/fsl/qe/qe.c
drivers/soc/fsl/qe/qe_common.c
drivers/soc/fsl/qe/qe_tdm.c
drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/cpu_cooling.c
drivers/thermal/db8500_thermal.c
drivers/thermal/devfreq_cooling.c
drivers/thermal/gov_bang_bang.c
drivers/thermal/hisi_thermal.c
drivers/thermal/imx_thermal.c
drivers/thermal/int340x_thermal/int3402_thermal.c
drivers/thermal/int340x_thermal/int3403_thermal.c
drivers/thermal/int340x_thermal/int340x_thermal_zone.c
drivers/thermal/int340x_thermal/int340x_thermal_zone.h
drivers/thermal/int340x_thermal/processor_thermal_device.c
drivers/thermal/intel_bxt_pmic_thermal.c [new file with mode: 0644]
drivers/thermal/intel_soc_dts_iosf.c
drivers/thermal/max77620_thermal.c [new file with mode: 0644]
drivers/thermal/mtk_thermal.c
drivers/thermal/of-thermal.c
drivers/thermal/qcom-spmi-temp-alarm.c
drivers/thermal/qcom/Kconfig [new file with mode: 0644]
drivers/thermal/qcom/Makefile [new file with mode: 0644]
drivers/thermal/qcom/tsens-8916.c [new file with mode: 0644]
drivers/thermal/qcom/tsens-8960.c [new file with mode: 0644]
drivers/thermal/qcom/tsens-8974.c [new file with mode: 0644]
drivers/thermal/qcom/tsens-8996.c [new file with mode: 0644]
drivers/thermal/qcom/tsens-common.c [new file with mode: 0644]
drivers/thermal/qcom/tsens.c [new file with mode: 0644]
drivers/thermal/qcom/tsens.h [new file with mode: 0644]
drivers/thermal/qoriq_thermal.c [new file with mode: 0644]
drivers/thermal/rcar_thermal.c
drivers/thermal/rockchip_thermal.c
drivers/thermal/samsung/exynos_tmu.c
drivers/thermal/st/st_thermal_memmap.c
drivers/thermal/tango_thermal.c
drivers/thermal/tegra/soctherm.c
drivers/thermal/tegra/soctherm.h
drivers/thermal/tegra/tegra124-soctherm.c
drivers/thermal/tegra/tegra132-soctherm.c
drivers/thermal/tegra/tegra210-soctherm.c
drivers/thermal/thermal_core.c
drivers/thermal/ti-soc-thermal/ti-thermal-common.c
drivers/thermal/user_space.c
drivers/thermal/x86_pkg_temp_thermal.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-sead3.c [deleted file]
drivers/video/fbdev/Kconfig
drivers/video/fbdev/Makefile
drivers/video/fbdev/amba-clcd-nomadik.c [new file with mode: 0644]
drivers/video/fbdev/amba-clcd-nomadik.h [new file with mode: 0644]
drivers/video/fbdev/amba-clcd-versatile.c
drivers/video/fbdev/amba-clcd-versatile.h [new file with mode: 0644]
drivers/video/fbdev/amba-clcd.c
drivers/video/fbdev/arcfb.c
drivers/video/fbdev/asiliantfb.c
drivers/video/fbdev/aty/aty128fb.c
drivers/video/fbdev/aty/atyfb_base.c
drivers/video/fbdev/aty/radeon_monitor.c
drivers/video/fbdev/bfin_adv7393fb.c
drivers/video/fbdev/cobalt_lcdfb.c
drivers/video/fbdev/efifb.c
drivers/video/fbdev/exynos/Kconfig [deleted file]
drivers/video/fbdev/exynos/Makefile [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi.c [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h [deleted file]
drivers/video/fbdev/exynos/s6e8ax0.c [deleted file]
drivers/video/fbdev/hecubafb.c
drivers/video/fbdev/hgafb.c
drivers/video/fbdev/i740fb.c
drivers/video/fbdev/i810/i810_main.c
drivers/video/fbdev/intelfb/intelfbdrv.c
drivers/video/fbdev/kyro/fbdev.c
drivers/video/fbdev/matrox/matroxfb_Ti3026.c
drivers/video/fbdev/matrox/matroxfb_g450.c
drivers/video/fbdev/mb862xx/mb862xx-i2c.c
drivers/video/fbdev/mx3fb.c
drivers/video/fbdev/mxsfb.c
drivers/video/fbdev/offb.c
drivers/video/fbdev/omap/lcd_mipid.c
drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c
drivers/video/fbdev/omap2/omapfb/dss/dsi.c
drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c
drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c
drivers/video/fbdev/pm2fb.c
drivers/video/fbdev/pvr2fb.c
drivers/video/fbdev/pxafb.c
drivers/video/fbdev/s1d13xxxfb.c
drivers/video/fbdev/s3c2410fb.c
drivers/video/fbdev/s3c2410fb.h
drivers/video/fbdev/savage/savagefb_driver.c
drivers/video/fbdev/simplefb.c
drivers/video/fbdev/sm712fb.c
drivers/video/fbdev/smscufx.c
drivers/video/fbdev/ssd1307fb.c
drivers/video/fbdev/tdfxfb.c
drivers/video/fbdev/uvesafb.c
drivers/video/fbdev/vfb.c
drivers/video/fbdev/vga16fb.c
drivers/virt/fsl_hypervisor.c
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/asm9260_wdt.c
drivers/watchdog/bcm7038_wdt.c
drivers/watchdog/cadence_wdt.c
drivers/watchdog/dw_wdt.c
drivers/watchdog/hpwdt.c
drivers/watchdog/iTCO_wdt.c
drivers/watchdog/imx2_wdt.c
drivers/watchdog/kempld_wdt.c
drivers/watchdog/mt7621_wdt.c
drivers/watchdog/of_xilinx_wdt.c
drivers/watchdog/pretimeout_noop.c [new file with mode: 0644]
drivers/watchdog/pretimeout_panic.c [new file with mode: 0644]
drivers/watchdog/rn5t618_wdt.c
drivers/watchdog/rt2880_wdt.c
drivers/watchdog/softdog.c
drivers/watchdog/st_lpc_wdt.c
drivers/watchdog/tegra_wdt.c
drivers/watchdog/txx9wdt.c
drivers/watchdog/w83627hf_wdt.c
drivers/watchdog/watchdog_core.c
drivers/watchdog/watchdog_dev.c
drivers/watchdog/watchdog_pretimeout.c [new file with mode: 0644]
drivers/watchdog/watchdog_pretimeout.h [new file with mode: 0644]
drivers/watchdog/ziirave_wdt.c
fs/befs/befs.h
fs/befs/btree.c
fs/befs/datastream.c
fs/befs/debug.c
fs/befs/io.c
fs/befs/io.h
fs/befs/linuxvfs.c
fs/befs/super.c
fs/btrfs/compression.c
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/free-space-tree.c
fs/btrfs/tests/extent-io-tests.c
fs/btrfs/tests/free-space-tree-tests.c
fs/cifs/cifs_debug.c
fs/cifs/cifs_fs_sb.h
fs/cifs/cifs_ioctl.h
fs/cifs/cifsacl.c
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/file.c
fs/cifs/ioctl.c
fs/cifs/misc.c
fs/cifs/readdir.c
fs/cifs/smb2inode.c
fs/cifs/smb2misc.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h
fs/cifs/xattr.c
fs/compat_ioctl.c
fs/exec.c
fs/exportfs/expfs.c
fs/f2fs/gc.c
fs/kernfs/dir.c
fs/locks.c
fs/namei.c
fs/namespace.c
fs/nfs/cache_lib.c
fs/nfs/callback.c
fs/nfs/callback.h
fs/nfs/callback_proc.c
fs/nfs/callback_xdr.c
fs/nfs/client.c
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/dir.c
fs/nfs/direct.c
fs/nfs/file.c
fs/nfs/flexfilelayout/flexfilelayout.c
fs/nfs/internal.h
fs/nfs/netns.h
fs/nfs/nfs42proc.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4client.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4session.h
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
fs/nfs/pnfs.c
fs/nfs/pnfs.h
fs/nfs/pnfs_nfs.c
fs/nfs/super.c
fs/nfsd/flexfilelayout.c
fs/nfsd/netns.h
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4layouts.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsctl.c
fs/nfsd/nfsproc.c
fs/nfsd/nfssvc.c
fs/nfsd/pnfs.h
fs/nfsd/state.h
fs/nfsd/vfs.c
fs/nfsd/vfs.h
fs/nfsd/xdr4.h
fs/nfsd/xdr4cb.h
fs/open.c
fs/overlayfs/copy_up.c
fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/super.c
fs/proc/base.c
fs/read_write.c
fs/super.c
fs/sysfs/dir.c
fs/xfs/Makefile
fs/xfs/libxfs/xfs_ag_resv.c
fs/xfs/libxfs/xfs_alloc.c
fs/xfs/libxfs/xfs_bmap.c
fs/xfs/libxfs/xfs_bmap.h
fs/xfs/libxfs/xfs_bmap_btree.c
fs/xfs/libxfs/xfs_btree.c
fs/xfs/libxfs/xfs_btree.h
fs/xfs/libxfs/xfs_defer.h
fs/xfs/libxfs/xfs_format.h
fs/xfs/libxfs/xfs_fs.h
fs/xfs/libxfs/xfs_inode_buf.c
fs/xfs/libxfs/xfs_inode_buf.h
fs/xfs/libxfs/xfs_inode_fork.c
fs/xfs/libxfs/xfs_inode_fork.h
fs/xfs/libxfs/xfs_log_format.h
fs/xfs/libxfs/xfs_refcount.c [new file with mode: 0644]
fs/xfs/libxfs/xfs_refcount.h [new file with mode: 0644]
fs/xfs/libxfs/xfs_refcount_btree.c [new file with mode: 0644]
fs/xfs/libxfs/xfs_refcount_btree.h [new file with mode: 0644]
fs/xfs/libxfs/xfs_rmap.c
fs/xfs/libxfs/xfs_rmap.h
fs/xfs/libxfs/xfs_rmap_btree.c
fs/xfs/libxfs/xfs_rmap_btree.h
fs/xfs/libxfs/xfs_sb.c
fs/xfs/libxfs/xfs_shared.h
fs/xfs/libxfs/xfs_trans_resv.c
fs/xfs/libxfs/xfs_trans_resv.h
fs/xfs/libxfs/xfs_trans_space.h
fs/xfs/libxfs/xfs_types.h
fs/xfs/xfs_aops.c
fs/xfs/xfs_aops.h
fs/xfs/xfs_bmap_item.c [new file with mode: 0644]
fs/xfs/xfs_bmap_item.h [new file with mode: 0644]
fs/xfs/xfs_bmap_util.c
fs/xfs/xfs_dir2_readdir.c
fs/xfs/xfs_error.h
fs/xfs/xfs_file.c
fs/xfs/xfs_fsops.c
fs/xfs/xfs_fsops.h
fs/xfs/xfs_globals.c
fs/xfs/xfs_icache.c
fs/xfs/xfs_icache.h
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_inode_item.c
fs/xfs/xfs_ioctl.c
fs/xfs/xfs_iomap.c
fs/xfs/xfs_iomap.h
fs/xfs/xfs_iops.c
fs/xfs/xfs_itable.c
fs/xfs/xfs_linux.h
fs/xfs/xfs_log_recover.c
fs/xfs/xfs_mount.c
fs/xfs/xfs_mount.h
fs/xfs/xfs_ondisk.h
fs/xfs/xfs_pnfs.c
fs/xfs/xfs_refcount_item.c [new file with mode: 0644]
fs/xfs/xfs_refcount_item.h [new file with mode: 0644]
fs/xfs/xfs_reflink.c [new file with mode: 0644]
fs/xfs/xfs_reflink.h [new file with mode: 0644]
fs/xfs/xfs_rmap_item.c
fs/xfs/xfs_stats.c
fs/xfs/xfs_stats.h
fs/xfs/xfs_super.c
fs/xfs/xfs_sysctl.c
fs/xfs/xfs_sysctl.h
fs/xfs/xfs_trace.h
fs/xfs/xfs_trans.h
fs/xfs/xfs_trans_bmap.c [new file with mode: 0644]
fs/xfs/xfs_trans_refcount.c [new file with mode: 0644]
fs/xfs/xfs_trans_rmap.c
include/asm-generic/export.h [new file with mode: 0644]
include/asm-generic/libata-portmap.h [deleted file]
include/asm-generic/percpu.h
include/asm-generic/vmlinux.lds.h
include/dt-bindings/thermal/tegra124-soctherm.h
include/linux/acpi.h
include/linux/amba/clcd.h
include/linux/ata.h
include/linux/blk-cgroup.h
include/linux/cgroup.h
include/linux/compiler-gcc.h
include/linux/compiler.h
include/linux/cpufreq.h
include/linux/export.h
include/linux/exportfs.h
include/linux/falloc.h
include/linux/fdtable.h
include/linux/fs.h
include/linux/genhd.h
include/linux/init.h
include/linux/kasan.h
include/linux/kernfs.h
include/linux/libata.h
include/linux/mlx5/device.h
include/linux/mm.h
include/linux/nfs4.h
include/linux/nfs_fs_sb.h
include/linux/nfs_xdr.h
include/linux/pkeys.h
include/linux/pwm.h
include/linux/random.h
include/linux/sunrpc/auth.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/rpc_rdma.h
include/linux/sunrpc/sched.h
include/linux/sunrpc/svc_rdma.h
include/linux/sunrpc/xdr.h
include/linux/sunrpc/xprt.h
include/linux/sunrpc/xprtmultipath.h
include/linux/sunrpc/xprtrdma.h
include/linux/syscalls.h
include/linux/thermal.h
include/linux/watchdog.h
include/net/cfg80211.h
include/net/l3mdev.h
include/soc/fsl/bman.h [new file with mode: 0644]
include/soc/fsl/qman.h [new file with mode: 0644]
include/trace/events/cgroup.h [new file with mode: 0644]
include/uapi/asm-generic/unistd.h
include/uapi/linux/btrfs.h
include/uapi/linux/falloc.h
include/uapi/linux/fs.h
include/uapi/linux/nfs4.h
include/uapi/linux/pci_regs.h
include/uapi/rdma/qedr-abi.h [new file with mode: 0644]
include/video/exynos_mipi_dsim.h [deleted file]
init/Makefile
init/main.c
kernel/cgroup.c
kernel/cpu.c
kernel/cpuset.c
kernel/events/uprobes.c
kernel/fork.c
kernel/printk/printk.c
kernel/ptrace.c
kernel/rcu/tiny.c
kernel/rcu/tree.c
kernel/sched/debug.c
kernel/sched/fair.c
kernel/softirq.c
kernel/time/alarmtimer.c
kernel/time/timer.c
kernel/trace/Makefile
lib/Kconfig.debug
lib/iov_iter.c
lib/irq_poll.c
lib/percpu-refcount.c
lib/random32.c
mm/Makefile
mm/frame_vector.c
mm/gup.c
mm/kasan/kasan.c
mm/memory.c
mm/mempolicy.c
mm/nommu.c
mm/page_alloc.c
mm/percpu.c
mm/process_vm_access.c
mm/util.c
net/bridge/br_sysfs_if.c
net/ceph/pagevec.c
net/core/dev.c
net/core/rtnetlink.c
net/ipv4/route.c
net/ipv6/tcp_ipv6.c
net/openvswitch/flow.c
net/openvswitch/vport-internal_dev.c
net/openvswitch/vport.c
net/sched/act_api.c
net/sched/cls_api.c
net/strparser/strparser.c
net/sunrpc/auth.c
net/sunrpc/auth_generic.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/auth_unix.c
net/sunrpc/backchannel_rqst.c
net/sunrpc/cache.c
net/sunrpc/clnt.c
net/sunrpc/sched.c
net/sunrpc/svc.c
net/sunrpc/xdr.c
net/sunrpc/xprt.c
net/sunrpc/xprtmultipath.c
net/sunrpc/xprtrdma/backchannel.c
net/sunrpc/xprtrdma/fmr_ops.c
net/sunrpc/xprtrdma/frwr_ops.c
net/sunrpc/xprtrdma/rpc_rdma.c
net/sunrpc/xprtrdma/svc_rdma_backchannel.c
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
net/sunrpc/xprtrdma/svc_rdma_sendto.c
net/sunrpc/xprtrdma/svc_rdma_transport.c
net/sunrpc/xprtrdma/transport.c
net/sunrpc/xprtrdma/verbs.c
net/sunrpc/xprtrdma/xprt_rdma.h
net/sunrpc/xprtsock.c
net/tipc/udp_media.c
samples/Kconfig
samples/Makefile
samples/auxdisplay/.gitignore [moved from Documentation/auxdisplay/.gitignore with 100% similarity]
samples/auxdisplay/Makefile [new file with mode: 0644]
samples/auxdisplay/cfag12864b-example.c [moved from Documentation/auxdisplay/cfag12864b-example.c with 100% similarity]
samples/blackfin/Makefile [new file with mode: 0644]
samples/blackfin/gptimers-example.c [moved from Documentation/blackfin/gptimers-example.c with 100% similarity]
samples/mei/.gitignore [moved from Documentation/misc-devices/mei/.gitignore with 100% similarity]
samples/mei/Makefile [new file with mode: 0644]
samples/mei/TODO [moved from Documentation/misc-devices/mei/TODO with 100% similarity]
samples/mei/mei-amt-version.c [moved from Documentation/misc-devices/mei/mei-amt-version.c with 100% similarity]
samples/mic/mpssd/.gitignore [moved from Documentation/mic/mpssd/.gitignore with 100% similarity]
samples/mic/mpssd/Makefile [new file with mode: 0644]
samples/mic/mpssd/micctrl [moved from Documentation/mic/mpssd/micctrl with 100% similarity]
samples/mic/mpssd/mpss [moved from Documentation/mic/mpssd/mpss with 100% similarity]
samples/mic/mpssd/mpssd.c [moved from Documentation/mic/mpssd/mpssd.c with 100% similarity]
samples/mic/mpssd/mpssd.h [moved from Documentation/mic/mpssd/mpssd.h with 100% similarity]
samples/mic/mpssd/sysfs.c [moved from Documentation/mic/mpssd/sysfs.c with 100% similarity]
samples/timers/.gitignore [moved from Documentation/timers/.gitignore with 100% similarity]
samples/timers/Makefile [new file with mode: 0644]
samples/timers/hpet_example.c [moved from Documentation/timers/hpet_example.c with 100% similarity]
samples/watchdog/.gitignore [moved from Documentation/watchdog/src/.gitignore with 53% similarity]
samples/watchdog/Makefile [new file with mode: 0644]
samples/watchdog/watchdog-simple.c [moved from Documentation/watchdog/src/watchdog-simple.c with 100% similarity]
scripts/Makefile.build
scripts/Makefile.gcc-plugins
scripts/Makefile.modpost
scripts/basic/fixdep.c
scripts/coccicheck
scripts/coccinelle/api/memdup_user.cocci
scripts/coccinelle/api/pm_runtime.cocci
scripts/coccinelle/misc/cond_no_effect.cocci [new file with mode: 0644]
scripts/gcc-plugins/latent_entropy_plugin.c [new file with mode: 0644]
scripts/gen_initramfs_list.sh
scripts/genksyms/lex.l
scripts/genksyms/lex.lex.c_shipped
scripts/link-vmlinux.sh
security/tomoyo/domain.c
sound/core/seq/seq_compat.c
sound/pci/hda/dell_wmi_helper.c
sound/pci/hda/thinkpad_helper.c
sound/usb/line6/driver.c
sound/usb/line6/podhd.c
tools/accounting/.gitignore [moved from Documentation/accounting/.gitignore with 100% similarity]
tools/accounting/Makefile [new file with mode: 0644]
tools/accounting/getdelays.c [moved from Documentation/accounting/getdelays.c with 100% similarity]
tools/laptop/dslm/.gitignore [moved from Documentation/laptops/.gitignore with 100% similarity]
tools/laptop/dslm/Makefile [new file with mode: 0644]
tools/laptop/dslm/dslm.c [moved from Documentation/laptops/dslm.c with 100% similarity]
tools/objtool/arch/x86/decode.c
tools/objtool/builtin-check.c
tools/pcmcia/.gitignore [moved from Documentation/pcmcia/.gitignore with 100% similarity]
tools/pcmcia/Makefile [new file with mode: 0644]
tools/pcmcia/crc32hash.c [moved from Documentation/pcmcia/crc32hash.c with 100% similarity]
tools/perf/jvmti/Makefile
tools/perf/ui/browsers/hists.c
tools/perf/util/header.c
tools/perf/util/parse-events.l
tools/testing/selftests/filesystems/.gitignore [moved from Documentation/filesystems/.gitignore with 100% similarity]
tools/testing/selftests/filesystems/Makefile [new file with mode: 0644]
tools/testing/selftests/filesystems/dnotify_test.c [moved from Documentation/filesystems/dnotify_test.c with 100% similarity]
tools/testing/selftests/futex/functional/run.sh
tools/testing/selftests/futex/run.sh
tools/testing/selftests/ia64/.gitignore [moved from Documentation/ia64/.gitignore with 100% similarity]
tools/testing/selftests/ia64/Makefile [new file with mode: 0644]
tools/testing/selftests/ia64/aliasing-test.c [moved from Documentation/ia64/aliasing-test.c with 100% similarity]
tools/testing/selftests/networking/timestamping/.gitignore [moved from Documentation/networking/timestamping/.gitignore with 100% similarity]
tools/testing/selftests/networking/timestamping/Makefile [new file with mode: 0644]
tools/testing/selftests/networking/timestamping/hwtstamp_config.c [moved from Documentation/networking/timestamping/hwtstamp_config.c with 100% similarity]
tools/testing/selftests/networking/timestamping/timestamping.c [moved from Documentation/networking/timestamping/timestamping.c with 100% similarity]
tools/testing/selftests/networking/timestamping/txtimestamp.c [moved from Documentation/networking/timestamping/txtimestamp.c with 100% similarity]
tools/testing/selftests/powerpc/copyloops/asm/export.h [new file with mode: 0644]
tools/testing/selftests/powerpc/math/.gitignore
tools/testing/selftests/powerpc/signal/.gitignore [new file with mode: 0644]
tools/testing/selftests/powerpc/stringloops/asm/export.h [new file with mode: 0644]
tools/testing/selftests/powerpc/tm/.gitignore
tools/testing/selftests/prctl/.gitignore [moved from Documentation/prctl/.gitignore with 100% similarity]
tools/testing/selftests/prctl/Makefile [new file with mode: 0644]
tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c [moved from Documentation/prctl/disable-tsc-ctxt-sw-stress-test.c with 100% similarity]
tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c [moved from Documentation/prctl/disable-tsc-on-off-stress-test.c with 100% similarity]
tools/testing/selftests/prctl/disable-tsc-test.c [moved from Documentation/prctl/disable-tsc-test.c with 100% similarity]
tools/testing/selftests/ptp/.gitignore [moved from Documentation/ptp/.gitignore with 100% similarity]
tools/testing/selftests/ptp/Makefile [new file with mode: 0644]
tools/testing/selftests/ptp/testptp.c [moved from Documentation/ptp/testptp.c with 100% similarity]
tools/testing/selftests/ptp/testptp.mk [moved from Documentation/ptp/testptp.mk with 100% similarity]
tools/testing/selftests/timers/posix_timers.c
tools/testing/selftests/vDSO/.gitignore [moved from Documentation/vDSO/.gitignore with 100% similarity]
tools/testing/selftests/vDSO/Makefile [new file with mode: 0644]
tools/testing/selftests/vDSO/parse_vdso.c [moved from Documentation/vDSO/parse_vdso.c with 100% similarity]
tools/testing/selftests/vDSO/vdso_standalone_test_x86.c [moved from Documentation/vDSO/vdso_standalone_test_x86.c with 100% similarity]
tools/testing/selftests/vDSO/vdso_test.c [moved from Documentation/vDSO/vdso_test.c with 100% similarity]
tools/testing/selftests/watchdog/.gitignore [new file with mode: 0644]
tools/testing/selftests/watchdog/Makefile [new file with mode: 0644]
tools/testing/selftests/watchdog/watchdog-test.c [moved from Documentation/watchdog/src/watchdog-test.c with 100% similarity]
tools/testing/selftests/zram/README
virt/kvm/async_pf.c
virt/kvm/kvm_main.c

index 2408e56e241ba199593f56d96d8e43a5a407a103..02d261407683dcfa483cf15247b2cf31cff0432a 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -127,6 +127,7 @@ Peter Oruba <peter@oruba.de>
 Peter Oruba <peter.oruba@amd.com>
 Pratyush Anand <pratyush.anand@gmail.com> <pratyush.anand@st.com>
 Praveen BP <praveenbp@ti.com>
+Qais Yousef <qsyousef@gmail.com> <qais.yousef@imgtec.com>
 Rajesh Shah <rajesh.shah@intel.com>
 Ralf Baechle <ralf@linux-mips.org>
 Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
index cb9a6c6fa83b2288792d10e046cfd56c0ba668b5..3acc4f1a6f8420552614eed779d414dd9a926520 100644 (file)
@@ -46,7 +46,8 @@ IRQ.txt
 Intel-IOMMU.txt
        - basic info on the Intel IOMMU virtualization support.
 Makefile
-       - some files in Documentation dir are actually sample code to build
+       - This file does nothing. Removing it breaks make htmldocs and
+         make distclean.
 ManagementStyle
        - how to (attempt to) manage kernel hackers.
 RCU/
diff --git a/Documentation/80211/cfg80211.rst b/Documentation/80211/cfg80211.rst
new file mode 100644 (file)
index 0000000..b1e149e
--- /dev/null
@@ -0,0 +1,345 @@
+==================
+cfg80211 subsystem
+==================
+
+Device registration
+===================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Device registration
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_channel_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_channel
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_rate_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_rate
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_sta_ht_cap
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_supported_band
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_signal_type
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_params_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wireless_dev
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_new
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_register
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_unregister
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_free
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_name
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_dev
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_priv
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: priv_to_wiphy
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: set_wiphy_dev
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wdev_priv
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_iface_limit
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_iface_combination
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_check_combinations
+
+Actions and configuration
+=========================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Actions and configuration
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ops
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: vif_params
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: key_params
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: survey_info_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: survey_info
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_beacon_data
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ap_settings
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: station_parameters
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: rate_info_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: rate_info
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: station_info
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: monitor_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: mpath_info_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: mpath_info
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: bss_parameters
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_txq_params
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_crypto_settings
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_auth_request
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_assoc_request
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_deauth_request
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_disassoc_request
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ibss_params
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_connect_params
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_pmksa
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_rx_mlme_mgmt
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_auth_timeout
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_rx_assoc_resp
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_assoc_timeout
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_tx_mlme_mgmt
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ibss_joined
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_connect_result
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_connect_bss
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_connect_timeout
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_roamed
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_disconnected
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ready_on_channel
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_remain_on_channel_expired
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_new_sta
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_rx_mgmt
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_mgmt_tx_status
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_cqm_rssi_notify
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_cqm_pktloss_notify
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_michael_mic_failure
+
+Scanning and BSS list handling
+==============================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Scanning and BSS list handling
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ssid
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_scan_request
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_scan_done
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_bss
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_inform_bss
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_inform_bss_frame_data
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_inform_bss_data
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_unlink_bss
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_find_ie
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_bss_get_ie
+
+Utility functions
+=================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Utility functions
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_channel_to_frequency
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_frequency_to_channel
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_get_channel
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_get_response_rate
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_hdrlen
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_get_hdrlen_from_skb
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_radiotap_iterator
+
+Data path helpers
+=================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Data path helpers
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_data_to_8023
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_data_from_8023
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_amsdu_to_8023s
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_classify8021d
+
+Regulatory enforcement infrastructure
+=====================================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Regulatory enforcement infrastructure
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: regulatory_hint
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_apply_custom_regulatory
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: freq_reg_info
+
+RFkill integration
+==================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: RFkill integration
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_rfkill_set_hw_state
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_rfkill_start_polling
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_rfkill_stop_polling
+
+Test mode
+=========
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Test mode
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_testmode_alloc_reply_skb
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_testmode_reply
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_testmode_alloc_event_skb
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_testmode_event
diff --git a/Documentation/80211/conf.py b/Documentation/80211/conf.py
new file mode 100644 (file)
index 0000000..20c7c27
--- /dev/null
@@ -0,0 +1,5 @@
+# -*- coding: utf-8; mode: python -*-
+
+project = "Linux 802.11 Driver Developer's Guide"
+
+tags.add("subproject")
diff --git a/Documentation/80211/index.rst b/Documentation/80211/index.rst
new file mode 100644 (file)
index 0000000..90bba47
--- /dev/null
@@ -0,0 +1,17 @@
+=====================================
+Linux 802.11 Driver Developer's Guide
+=====================================
+
+.. toctree::
+
+   introduction
+   cfg80211
+   mac80211
+   mac80211-advanced
+
+.. only::  subproject
+
+   Indices
+   =======
+
+   * :ref:`genindex`
diff --git a/Documentation/80211/introduction.rst b/Documentation/80211/introduction.rst
new file mode 100644 (file)
index 0000000..4938fa8
--- /dev/null
@@ -0,0 +1,17 @@
+============
+Introduction
+============
+
+Explaining wireless 802.11 networking in the Linux kernel
+
+Copyright 2007-2009 Johannes Berg
+
+These books attempt to give a description of the various subsystems
+that play a role in 802.11 wireless networking in Linux. Since these
+books are for kernel developers they attempts to document the
+structures and functions used in the kernel as well as giving a
+higher-level overview.
+
+The reader is expected to be familiar with the 802.11 standard as
+published by the IEEE in 802.11-2007 (or possibly later versions).
+References to this standard will be given as "802.11-2007 8.1.5".
diff --git a/Documentation/80211/mac80211-advanced.rst b/Documentation/80211/mac80211-advanced.rst
new file mode 100644 (file)
index 0000000..70a89b2
--- /dev/null
@@ -0,0 +1,295 @@
+=============================
+mac80211 subsystem (advanced)
+=============================
+
+Information contained within this part of the book is of interest only
+for advanced interaction of mac80211 with drivers to exploit more
+hardware capabilities and improve performance.
+
+LED support
+===========
+
+Mac80211 supports various ways of blinking LEDs. Wherever possible,
+device LEDs should be exposed as LED class devices and hooked up to the
+appropriate trigger, which will then be triggered appropriately by
+mac80211.
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_tx_led_name
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_rx_led_name
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_assoc_led_name
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_radio_led_name
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tpt_blink
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tpt_led_trigger_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_create_tpt_led_trigger
+
+Hardware crypto acceleration
+============================
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Hardware crypto acceleration
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: set_key_cmd
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_key_conf
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_key_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_tkip_p1k
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_tkip_p1k_iv
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_tkip_p2k
+
+Powersave support
+=================
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Powersave support
+
+Beacon filter support
+=====================
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Beacon filter support
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_beacon_loss
+
+Multiple queues and QoS support
+===============================
+
+TBD
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_queue_params
+
+Access point mode support
+=========================
+
+TBD
+
+Some parts of the if_conf should be discussed here instead
+
+Insert notes about VLAN interfaces with hw crypto here or in the hw
+crypto chapter.
+
+support for powersaving clients
+-------------------------------
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: AP support for powersaving clients
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_buffered_bc
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_beacon_get
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta_eosp
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_frame_release_type
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta_ps_transition
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta_ps_transition_ni
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta_set_buffered
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta_block_awake
+
+Supporting multiple virtual interfaces
+======================================
+
+TBD
+
+Note: WDS with identical MAC address should almost always be OK
+
+Insert notes about having multiple virtual interfaces with different MAC
+addresses here, note which configurations are supported by mac80211, add
+notes about supporting hw crypto with it.
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_iterate_active_interfaces
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_iterate_active_interfaces_atomic
+
+Station handling
+================
+
+TODO
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: sta_notify_cmd
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_find_sta
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_find_sta_by_ifaddr
+
+Hardware scan offload
+=====================
+
+TBD
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_scan_completed
+
+Aggregation
+===========
+
+TX A-MPDU aggregation
+---------------------
+
+.. kernel-doc:: net/mac80211/agg-tx.c
+   :doc: TX A-MPDU aggregation
+
+.. WARNING: DOCPROC directive not supported: !Cnet/mac80211/agg-tx.c
+
+RX A-MPDU aggregation
+---------------------
+
+.. kernel-doc:: net/mac80211/agg-rx.c
+   :doc: RX A-MPDU aggregation
+
+.. WARNING: DOCPROC directive not supported: !Cnet/mac80211/agg-rx.c
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_ampdu_mlme_action
+
+Spatial Multiplexing Powersave (SMPS)
+=====================================
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Spatial multiplexing power save
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_request_smps
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_smps_mode
+
+TBD
+
+This part of the book describes the rate control algorithm interface and
+how it relates to mac80211 and drivers.
+
+Rate Control API
+================
+
+TBD
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_start_tx_ba_session
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_start_tx_ba_cb_irqsafe
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_stop_tx_ba_session
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_stop_tx_ba_cb_irqsafe
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rate_control_changed
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_rate_control
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: rate_control_send_low
+
+TBD
+
+This part of the book describes mac80211 internals.
+
+Key handling
+============
+
+Key handling basics
+-------------------
+
+.. kernel-doc:: net/mac80211/key.c
+   :doc: Key handling basics
+
+MORE TBD
+--------
+
+TBD
+
+Receive processing
+==================
+
+TBD
+
+Transmit processing
+===================
+
+TBD
+
+Station info handling
+=====================
+
+Programming information
+-----------------------
+
+.. kernel-doc:: net/mac80211/sta_info.h
+   :functions: sta_info
+
+.. kernel-doc:: net/mac80211/sta_info.h
+   :functions: ieee80211_sta_info_flags
+
+STA information lifetime rules
+------------------------------
+
+.. kernel-doc:: net/mac80211/sta_info.c
+   :doc: STA information lifetime rules
+
+Aggregation
+===========
+
+.. kernel-doc:: net/mac80211/sta_info.h
+   :functions: sta_ampdu_mlme
+
+.. kernel-doc:: net/mac80211/sta_info.h
+   :functions: tid_ampdu_tx
+
+.. kernel-doc:: net/mac80211/sta_info.h
+   :functions: tid_ampdu_rx
+
+Synchronisation
+===============
+
+TBD
+
+Locking, lots of RCU
diff --git a/Documentation/80211/mac80211.rst b/Documentation/80211/mac80211.rst
new file mode 100644 (file)
index 0000000..85a8335
--- /dev/null
@@ -0,0 +1,216 @@
+===========================
+mac80211 subsystem (basics)
+===========================
+
+You should read and understand the information contained within this
+part of the book while implementing a mac80211 driver. In some chapters,
+advanced usage is noted, those may be skipped if this isn't needed.
+
+This part of the book only covers station and monitor mode
+functionality, additional information required to implement the other
+modes is covered in the second part of the book.
+
+Basic hardware handling
+=======================
+
+TBD
+
+This chapter shall contain information on getting a hw struct allocated
+and registered with mac80211.
+
+Since it is required to allocate rates/modes before registering a hw
+struct, this chapter shall also contain information on setting up the
+rate/mode structs.
+
+Additionally, some discussion about the callbacks and the general
+programming model should be in here, including the definition of
+ieee80211_ops which will be referred to a lot.
+
+Finally, a discussion of hardware capabilities should be done with
+references to other parts of the book.
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_hw
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_hw_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: SET_IEEE80211_DEV
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: SET_IEEE80211_PERM_ADDR
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_ops
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_alloc_hw
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_register_hw
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_unregister_hw
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_free_hw
+
+PHY configuration
+=================
+
+TBD
+
+This chapter should describe PHY handling including start/stop callbacks
+and the various structures used.
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_conf
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_conf_flags
+
+Virtual interfaces
+==================
+
+TBD
+
+This chapter should describe virtual interface basics that are relevant
+to the driver (VLANs, MGMT etc are not.) It should explain the use of
+the add_iface/remove_iface callbacks as well as the interface
+configuration callbacks.
+
+Things related to AP mode should be discussed there.
+
+Things related to supporting multiple interfaces should be in the
+appropriate chapter, a BIG FAT note should be here about this though and
+the recommendation to allow only a single interface in STA mode at
+first!
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_vif
+
+Receive and transmit processing
+===============================
+
+what should be here
+-------------------
+
+TBD
+
+This should describe the receive and transmit paths in mac80211/the
+drivers as well as transmit status handling.
+
+Frame format
+------------
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Frame format
+
+Packet alignment
+----------------
+
+.. kernel-doc:: net/mac80211/rx.c
+   :doc: Packet alignment
+
+Calling into mac80211 from interrupts
+-------------------------------------
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Calling mac80211 from interrupts
+
+functions/definitions
+---------------------
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rx_status
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: mac80211_rx_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: mac80211_tx_info_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: mac80211_tx_control_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: mac80211_rate_control_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_rate
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_info
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_info_clear_status
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rx
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rx_ni
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rx_irqsafe
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_status
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_status_ni
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_status_irqsafe
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rts_get
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rts_duration
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_ctstoself_get
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_ctstoself_duration
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_generic_frame_duration
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_wake_queue
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_stop_queue
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_wake_queues
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_stop_queues
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_queue_stopped
+
+Frame filtering
+===============
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Frame filtering
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_filter_flags
+
+The mac80211 workqueue
+======================
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: mac80211 workqueue
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_queue_work
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_queue_delayed_work
diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl
deleted file mode 100644 (file)
index 800fe7a..0000000
+++ /dev/null
@@ -1,584 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE set PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
-       "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
-<set>
-  <setinfo>
-    <title>The 802.11 subsystems &ndash; for kernel developers</title>
-    <subtitle>
-      Explaining wireless 802.11 networking in the Linux kernel
-    </subtitle>
-
-    <copyright>
-      <year>2007-2009</year>
-      <holder>Johannes Berg</holder>
-    </copyright>
-
-    <authorgroup>
-      <author>
-        <firstname>Johannes</firstname>
-        <surname>Berg</surname>
-        <affiliation>
-          <address><email>johannes@sipsolutions.net</email></address>
-        </affiliation>
-      </author>
-    </authorgroup>
-
-    <legalnotice>
-      <para>
-        This documentation 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.
-      </para>
-      <para>
-        This documentation 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.
-      </para>
-      <para>
-        You should have received a copy of the GNU General Public
-        License along with this documentation; if not, write to the Free
-        Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-        MA 02111-1307 USA
-      </para>
-      <para>
-        For more details see the file COPYING in the source
-        distribution of Linux.
-      </para>
-    </legalnotice>
-
-    <abstract>
-      <para>
-        These books attempt to give a description of the
-        various subsystems that play a role in 802.11 wireless
-        networking in Linux. Since these books are for kernel
-        developers they attempts to document the structures
-        and functions used in the kernel as well as giving a
-        higher-level overview.
-      </para>
-      <para>
-       The reader is expected to be familiar with the 802.11
-       standard as published by the IEEE in 802.11-2007 (or
-       possibly later versions). References to this standard
-       will be given as "802.11-2007 8.1.5".
-      </para>
-    </abstract>
-  </setinfo>
-  <book id="cfg80211-developers-guide">
-    <bookinfo>
-      <title>The cfg80211 subsystem</title>
-
-      <abstract>
-!Pinclude/net/cfg80211.h Introduction
-      </abstract>
-    </bookinfo>
-      <chapter>
-      <title>Device registration</title>
-!Pinclude/net/cfg80211.h Device registration
-!Finclude/net/cfg80211.h ieee80211_channel_flags
-!Finclude/net/cfg80211.h ieee80211_channel
-!Finclude/net/cfg80211.h ieee80211_rate_flags
-!Finclude/net/cfg80211.h ieee80211_rate
-!Finclude/net/cfg80211.h ieee80211_sta_ht_cap
-!Finclude/net/cfg80211.h ieee80211_supported_band
-!Finclude/net/cfg80211.h cfg80211_signal_type
-!Finclude/net/cfg80211.h wiphy_params_flags
-!Finclude/net/cfg80211.h wiphy_flags
-!Finclude/net/cfg80211.h wiphy
-!Finclude/net/cfg80211.h wireless_dev
-!Finclude/net/cfg80211.h wiphy_new
-!Finclude/net/cfg80211.h wiphy_register
-!Finclude/net/cfg80211.h wiphy_unregister
-!Finclude/net/cfg80211.h wiphy_free
-
-!Finclude/net/cfg80211.h wiphy_name
-!Finclude/net/cfg80211.h wiphy_dev
-!Finclude/net/cfg80211.h wiphy_priv
-!Finclude/net/cfg80211.h priv_to_wiphy
-!Finclude/net/cfg80211.h set_wiphy_dev
-!Finclude/net/cfg80211.h wdev_priv
-!Finclude/net/cfg80211.h ieee80211_iface_limit
-!Finclude/net/cfg80211.h ieee80211_iface_combination
-!Finclude/net/cfg80211.h cfg80211_check_combinations
-      </chapter>
-      <chapter>
-      <title>Actions and configuration</title>
-!Pinclude/net/cfg80211.h Actions and configuration
-!Finclude/net/cfg80211.h cfg80211_ops
-!Finclude/net/cfg80211.h vif_params
-!Finclude/net/cfg80211.h key_params
-!Finclude/net/cfg80211.h survey_info_flags
-!Finclude/net/cfg80211.h survey_info
-!Finclude/net/cfg80211.h cfg80211_beacon_data
-!Finclude/net/cfg80211.h cfg80211_ap_settings
-!Finclude/net/cfg80211.h station_parameters
-!Finclude/net/cfg80211.h rate_info_flags
-!Finclude/net/cfg80211.h rate_info
-!Finclude/net/cfg80211.h station_info
-!Finclude/net/cfg80211.h monitor_flags
-!Finclude/net/cfg80211.h mpath_info_flags
-!Finclude/net/cfg80211.h mpath_info
-!Finclude/net/cfg80211.h bss_parameters
-!Finclude/net/cfg80211.h ieee80211_txq_params
-!Finclude/net/cfg80211.h cfg80211_crypto_settings
-!Finclude/net/cfg80211.h cfg80211_auth_request
-!Finclude/net/cfg80211.h cfg80211_assoc_request
-!Finclude/net/cfg80211.h cfg80211_deauth_request
-!Finclude/net/cfg80211.h cfg80211_disassoc_request
-!Finclude/net/cfg80211.h cfg80211_ibss_params
-!Finclude/net/cfg80211.h cfg80211_connect_params
-!Finclude/net/cfg80211.h cfg80211_pmksa
-!Finclude/net/cfg80211.h cfg80211_rx_mlme_mgmt
-!Finclude/net/cfg80211.h cfg80211_auth_timeout
-!Finclude/net/cfg80211.h cfg80211_rx_assoc_resp
-!Finclude/net/cfg80211.h cfg80211_assoc_timeout
-!Finclude/net/cfg80211.h cfg80211_tx_mlme_mgmt
-!Finclude/net/cfg80211.h cfg80211_ibss_joined
-!Finclude/net/cfg80211.h cfg80211_connect_result
-!Finclude/net/cfg80211.h cfg80211_connect_bss
-!Finclude/net/cfg80211.h cfg80211_connect_timeout
-!Finclude/net/cfg80211.h cfg80211_roamed
-!Finclude/net/cfg80211.h cfg80211_disconnected
-!Finclude/net/cfg80211.h cfg80211_ready_on_channel
-!Finclude/net/cfg80211.h cfg80211_remain_on_channel_expired
-!Finclude/net/cfg80211.h cfg80211_new_sta
-!Finclude/net/cfg80211.h cfg80211_rx_mgmt
-!Finclude/net/cfg80211.h cfg80211_mgmt_tx_status
-!Finclude/net/cfg80211.h cfg80211_cqm_rssi_notify
-!Finclude/net/cfg80211.h cfg80211_cqm_pktloss_notify
-!Finclude/net/cfg80211.h cfg80211_michael_mic_failure
-      </chapter>
-      <chapter>
-      <title>Scanning and BSS list handling</title>
-!Pinclude/net/cfg80211.h Scanning and BSS list handling
-!Finclude/net/cfg80211.h cfg80211_ssid
-!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
-!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
-      </chapter>
-      <chapter>
-      <title>Utility functions</title>
-!Pinclude/net/cfg80211.h Utility functions
-!Finclude/net/cfg80211.h ieee80211_channel_to_frequency
-!Finclude/net/cfg80211.h ieee80211_frequency_to_channel
-!Finclude/net/cfg80211.h ieee80211_get_channel
-!Finclude/net/cfg80211.h ieee80211_get_response_rate
-!Finclude/net/cfg80211.h ieee80211_hdrlen
-!Finclude/net/cfg80211.h ieee80211_get_hdrlen_from_skb
-!Finclude/net/cfg80211.h ieee80211_radiotap_iterator
-      </chapter>
-      <chapter>
-      <title>Data path helpers</title>
-!Pinclude/net/cfg80211.h Data path helpers
-!Finclude/net/cfg80211.h ieee80211_data_to_8023
-!Finclude/net/cfg80211.h ieee80211_data_from_8023
-!Finclude/net/cfg80211.h ieee80211_amsdu_to_8023s
-!Finclude/net/cfg80211.h cfg80211_classify8021d
-      </chapter>
-      <chapter>
-      <title>Regulatory enforcement infrastructure</title>
-!Pinclude/net/cfg80211.h Regulatory enforcement infrastructure
-!Finclude/net/cfg80211.h regulatory_hint
-!Finclude/net/cfg80211.h wiphy_apply_custom_regulatory
-!Finclude/net/cfg80211.h freq_reg_info
-      </chapter>
-      <chapter>
-      <title>RFkill integration</title>
-!Pinclude/net/cfg80211.h RFkill integration
-!Finclude/net/cfg80211.h wiphy_rfkill_set_hw_state
-!Finclude/net/cfg80211.h wiphy_rfkill_start_polling
-!Finclude/net/cfg80211.h wiphy_rfkill_stop_polling
-      </chapter>
-      <chapter>
-      <title>Test mode</title>
-!Pinclude/net/cfg80211.h Test mode
-!Finclude/net/cfg80211.h cfg80211_testmode_alloc_reply_skb
-!Finclude/net/cfg80211.h cfg80211_testmode_reply
-!Finclude/net/cfg80211.h cfg80211_testmode_alloc_event_skb
-!Finclude/net/cfg80211.h cfg80211_testmode_event
-      </chapter>
-  </book>
-  <book id="mac80211-developers-guide">
-    <bookinfo>
-      <title>The mac80211 subsystem</title>
-      <abstract>
-!Pinclude/net/mac80211.h Introduction
-!Pinclude/net/mac80211.h Warning
-      </abstract>
-    </bookinfo>
-
-    <toc></toc>
-
-  <!--
-  Generally, this document shall be ordered by increasing complexity.
-  It is important to note that readers should be able to read only
-  the first few sections to get a working driver and only advanced
-  usage should require reading the full document.
-  -->
-
-    <part>
-      <title>The basic mac80211 driver interface</title>
-      <partintro>
-        <para>
-          You should read and understand the information contained
-          within this part of the book while implementing a driver.
-          In some chapters, advanced usage is noted, that may be
-          skipped at first.
-        </para>
-        <para>
-          This part of the book only covers station and monitor mode
-          functionality, additional information required to implement
-          the other modes is covered in the second part of the book.
-        </para>
-      </partintro>
-
-      <chapter id="basics">
-        <title>Basic hardware handling</title>
-        <para>TBD</para>
-        <para>
-          This chapter shall contain information on getting a hw
-          struct allocated and registered with mac80211.
-        </para>
-        <para>
-          Since it is required to allocate rates/modes before registering
-          a hw struct, this chapter shall also contain information on setting
-          up the rate/mode structs.
-        </para>
-        <para>
-          Additionally, some discussion about the callbacks and
-          the general programming model should be in here, including
-          the definition of ieee80211_ops which will be referred to
-          a lot.
-        </para>
-        <para>
-          Finally, a discussion of hardware capabilities should be done
-          with references to other parts of the book.
-        </para>
-  <!-- intentionally multiple !F lines to get proper order -->
-!Finclude/net/mac80211.h ieee80211_hw
-!Finclude/net/mac80211.h ieee80211_hw_flags
-!Finclude/net/mac80211.h SET_IEEE80211_DEV
-!Finclude/net/mac80211.h SET_IEEE80211_PERM_ADDR
-!Finclude/net/mac80211.h ieee80211_ops
-!Finclude/net/mac80211.h ieee80211_alloc_hw
-!Finclude/net/mac80211.h ieee80211_register_hw
-!Finclude/net/mac80211.h ieee80211_unregister_hw
-!Finclude/net/mac80211.h ieee80211_free_hw
-      </chapter>
-
-      <chapter id="phy-handling">
-        <title>PHY configuration</title>
-        <para>TBD</para>
-        <para>
-          This chapter should describe PHY handling including
-          start/stop callbacks and the various structures used.
-        </para>
-!Finclude/net/mac80211.h ieee80211_conf
-!Finclude/net/mac80211.h ieee80211_conf_flags
-      </chapter>
-
-      <chapter id="iface-handling">
-        <title>Virtual interfaces</title>
-        <para>TBD</para>
-        <para>
-          This chapter should describe virtual interface basics
-          that are relevant to the driver (VLANs, MGMT etc are not.)
-          It should explain the use of the add_iface/remove_iface
-          callbacks as well as the interface configuration callbacks.
-        </para>
-        <para>Things related to AP mode should be discussed there.</para>
-        <para>
-          Things related to supporting multiple interfaces should be
-          in the appropriate chapter, a BIG FAT note should be here about
-          this though and the recommendation to allow only a single
-          interface in STA mode at first!
-        </para>
-!Finclude/net/mac80211.h ieee80211_vif
-      </chapter>
-
-      <chapter id="rx-tx">
-        <title>Receive and transmit processing</title>
-        <sect1>
-          <title>what should be here</title>
-          <para>TBD</para>
-          <para>
-            This should describe the receive and transmit
-            paths in mac80211/the drivers as well as
-            transmit status handling.
-          </para>
-        </sect1>
-        <sect1>
-          <title>Frame format</title>
-!Pinclude/net/mac80211.h Frame format
-        </sect1>
-        <sect1>
-          <title>Packet alignment</title>
-!Pnet/mac80211/rx.c Packet alignment
-        </sect1>
-        <sect1>
-          <title>Calling into mac80211 from interrupts</title>
-!Pinclude/net/mac80211.h Calling mac80211 from interrupts
-        </sect1>
-        <sect1>
-          <title>functions/definitions</title>
-!Finclude/net/mac80211.h ieee80211_rx_status
-!Finclude/net/mac80211.h mac80211_rx_flags
-!Finclude/net/mac80211.h mac80211_tx_info_flags
-!Finclude/net/mac80211.h mac80211_tx_control_flags
-!Finclude/net/mac80211.h mac80211_rate_control_flags
-!Finclude/net/mac80211.h ieee80211_tx_rate
-!Finclude/net/mac80211.h ieee80211_tx_info
-!Finclude/net/mac80211.h ieee80211_tx_info_clear_status
-!Finclude/net/mac80211.h ieee80211_rx
-!Finclude/net/mac80211.h ieee80211_rx_ni
-!Finclude/net/mac80211.h ieee80211_rx_irqsafe
-!Finclude/net/mac80211.h ieee80211_tx_status
-!Finclude/net/mac80211.h ieee80211_tx_status_ni
-!Finclude/net/mac80211.h ieee80211_tx_status_irqsafe
-!Finclude/net/mac80211.h ieee80211_rts_get
-!Finclude/net/mac80211.h ieee80211_rts_duration
-!Finclude/net/mac80211.h ieee80211_ctstoself_get
-!Finclude/net/mac80211.h ieee80211_ctstoself_duration
-!Finclude/net/mac80211.h ieee80211_generic_frame_duration
-!Finclude/net/mac80211.h ieee80211_wake_queue
-!Finclude/net/mac80211.h ieee80211_stop_queue
-!Finclude/net/mac80211.h ieee80211_wake_queues
-!Finclude/net/mac80211.h ieee80211_stop_queues
-!Finclude/net/mac80211.h ieee80211_queue_stopped
-        </sect1>
-      </chapter>
-
-      <chapter id="filters">
-        <title>Frame filtering</title>
-!Pinclude/net/mac80211.h Frame filtering
-!Finclude/net/mac80211.h ieee80211_filter_flags
-      </chapter>
-
-      <chapter id="workqueue">
-        <title>The mac80211 workqueue</title>
-!Pinclude/net/mac80211.h mac80211 workqueue
-!Finclude/net/mac80211.h ieee80211_queue_work
-!Finclude/net/mac80211.h ieee80211_queue_delayed_work
-      </chapter>
-    </part>
-
-    <part id="advanced">
-      <title>Advanced driver interface</title>
-      <partintro>
-        <para>
-         Information contained within this part of the book is
-         of interest only for advanced interaction of mac80211
-         with drivers to exploit more hardware capabilities and
-         improve performance.
-        </para>
-      </partintro>
-
-      <chapter id="led-support">
-        <title>LED support</title>
-        <para>
-         Mac80211 supports various ways of blinking LEDs. Wherever possible,
-         device LEDs should be exposed as LED class devices and hooked up to
-         the appropriate trigger, which will then be triggered appropriately
-         by mac80211.
-        </para>
-!Finclude/net/mac80211.h ieee80211_get_tx_led_name
-!Finclude/net/mac80211.h ieee80211_get_rx_led_name
-!Finclude/net/mac80211.h ieee80211_get_assoc_led_name
-!Finclude/net/mac80211.h ieee80211_get_radio_led_name
-!Finclude/net/mac80211.h ieee80211_tpt_blink
-!Finclude/net/mac80211.h ieee80211_tpt_led_trigger_flags
-!Finclude/net/mac80211.h ieee80211_create_tpt_led_trigger
-      </chapter>
-
-      <chapter id="hardware-crypto-offload">
-        <title>Hardware crypto acceleration</title>
-!Pinclude/net/mac80211.h Hardware crypto acceleration
-  <!-- intentionally multiple !F lines to get proper order -->
-!Finclude/net/mac80211.h set_key_cmd
-!Finclude/net/mac80211.h ieee80211_key_conf
-!Finclude/net/mac80211.h ieee80211_key_flags
-!Finclude/net/mac80211.h ieee80211_get_tkip_p1k
-!Finclude/net/mac80211.h ieee80211_get_tkip_p1k_iv
-!Finclude/net/mac80211.h ieee80211_get_tkip_p2k
-      </chapter>
-
-      <chapter id="powersave">
-        <title>Powersave support</title>
-!Pinclude/net/mac80211.h Powersave support
-      </chapter>
-
-      <chapter id="beacon-filter">
-        <title>Beacon filter support</title>
-!Pinclude/net/mac80211.h Beacon filter support
-!Finclude/net/mac80211.h ieee80211_beacon_loss
-      </chapter>
-
-      <chapter id="qos">
-        <title>Multiple queues and QoS support</title>
-        <para>TBD</para>
-!Finclude/net/mac80211.h ieee80211_tx_queue_params
-      </chapter>
-
-      <chapter id="AP">
-        <title>Access point mode support</title>
-        <para>TBD</para>
-        <para>Some parts of the if_conf should be discussed here instead</para>
-        <para>
-          Insert notes about VLAN interfaces with hw crypto here or
-          in the hw crypto chapter.
-        </para>
-      <section id="ps-client">
-        <title>support for powersaving clients</title>
-!Pinclude/net/mac80211.h AP support for powersaving clients
-!Finclude/net/mac80211.h ieee80211_get_buffered_bc
-!Finclude/net/mac80211.h ieee80211_beacon_get
-!Finclude/net/mac80211.h ieee80211_sta_eosp
-!Finclude/net/mac80211.h ieee80211_frame_release_type
-!Finclude/net/mac80211.h ieee80211_sta_ps_transition
-!Finclude/net/mac80211.h ieee80211_sta_ps_transition_ni
-!Finclude/net/mac80211.h ieee80211_sta_set_buffered
-!Finclude/net/mac80211.h ieee80211_sta_block_awake
-      </section>
-      </chapter>
-
-      <chapter id="multi-iface">
-        <title>Supporting multiple virtual interfaces</title>
-        <para>TBD</para>
-        <para>
-          Note: WDS with identical MAC address should almost always be OK
-        </para>
-        <para>
-          Insert notes about having multiple virtual interfaces with
-          different MAC addresses here, note which configurations are
-          supported by mac80211, add notes about supporting hw crypto
-          with it.
-        </para>
-!Finclude/net/mac80211.h ieee80211_iterate_active_interfaces
-!Finclude/net/mac80211.h ieee80211_iterate_active_interfaces_atomic
-      </chapter>
-
-      <chapter id="station-handling">
-        <title>Station handling</title>
-        <para>TODO</para>
-!Finclude/net/mac80211.h ieee80211_sta
-!Finclude/net/mac80211.h sta_notify_cmd
-!Finclude/net/mac80211.h ieee80211_find_sta
-!Finclude/net/mac80211.h ieee80211_find_sta_by_ifaddr
-      </chapter>
-
-      <chapter id="hardware-scan-offload">
-        <title>Hardware scan offload</title>
-        <para>TBD</para>
-!Finclude/net/mac80211.h ieee80211_scan_completed
-      </chapter>
-
-      <chapter id="aggregation">
-        <title>Aggregation</title>
-        <sect1>
-          <title>TX A-MPDU aggregation</title>
-!Pnet/mac80211/agg-tx.c TX A-MPDU aggregation
-!Cnet/mac80211/agg-tx.c
-        </sect1>
-        <sect1>
-          <title>RX A-MPDU aggregation</title>
-!Pnet/mac80211/agg-rx.c RX A-MPDU aggregation
-!Cnet/mac80211/agg-rx.c
-!Finclude/net/mac80211.h ieee80211_ampdu_mlme_action
-        </sect1>
-      </chapter>
-
-      <chapter id="smps">
-        <title>Spatial Multiplexing Powersave (SMPS)</title>
-!Pinclude/net/mac80211.h Spatial multiplexing power save
-!Finclude/net/mac80211.h ieee80211_request_smps
-!Finclude/net/mac80211.h ieee80211_smps_mode
-      </chapter>
-    </part>
-
-    <part id="rate-control">
-      <title>Rate control interface</title>
-      <partintro>
-        <para>TBD</para>
-        <para>
-         This part of the book describes the rate control algorithm
-         interface and how it relates to mac80211 and drivers.
-        </para>
-      </partintro>
-      <chapter id="ratecontrol-api">
-        <title>Rate Control API</title>
-        <para>TBD</para>
-!Finclude/net/mac80211.h ieee80211_start_tx_ba_session
-!Finclude/net/mac80211.h ieee80211_start_tx_ba_cb_irqsafe
-!Finclude/net/mac80211.h ieee80211_stop_tx_ba_session
-!Finclude/net/mac80211.h ieee80211_stop_tx_ba_cb_irqsafe
-!Finclude/net/mac80211.h ieee80211_rate_control_changed
-!Finclude/net/mac80211.h ieee80211_tx_rate_control
-!Finclude/net/mac80211.h rate_control_send_low
-      </chapter>
-    </part>
-
-    <part id="internal">
-      <title>Internals</title>
-      <partintro>
-        <para>TBD</para>
-        <para>
-         This part of the book describes mac80211 internals.
-        </para>
-      </partintro>
-
-      <chapter id="key-handling">
-        <title>Key handling</title>
-        <sect1>
-          <title>Key handling basics</title>
-!Pnet/mac80211/key.c Key handling basics
-        </sect1>
-        <sect1>
-          <title>MORE TBD</title>
-          <para>TBD</para>
-        </sect1>
-      </chapter>
-
-      <chapter id="rx-processing">
-        <title>Receive processing</title>
-        <para>TBD</para>
-      </chapter>
-
-      <chapter id="tx-processing">
-        <title>Transmit processing</title>
-        <para>TBD</para>
-      </chapter>
-
-      <chapter id="sta-info">
-        <title>Station info handling</title>
-        <sect1>
-          <title>Programming information</title>
-!Fnet/mac80211/sta_info.h sta_info
-!Fnet/mac80211/sta_info.h ieee80211_sta_info_flags
-        </sect1>
-        <sect1>
-          <title>STA information lifetime rules</title>
-!Pnet/mac80211/sta_info.c STA information lifetime rules
-        </sect1>
-      </chapter>
-
-      <chapter id="aggregation-internals">
-        <title>Aggregation</title>
-!Fnet/mac80211/sta_info.h sta_ampdu_mlme
-!Fnet/mac80211/sta_info.h tid_ampdu_tx
-!Fnet/mac80211/sta_info.h tid_ampdu_rx
-      </chapter>
-
-      <chapter id="synchronisation">
-        <title>Synchronisation</title>
-        <para>TBD</para>
-        <para>Locking, lots of RCU</para>
-      </chapter>
-    </part>
-  </book>
-</set>
index 736f5916daea1d8573ad53543dd16c9d20f4809f..fdf8232d0eeb28ffd361320a926674d461995e16 100644 (file)
@@ -12,7 +12,7 @@ DOCBOOKS := z8530book.xml  \
            kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
            gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
            genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
-           80211.xml debugobjects.xml sh.xml regulator.xml \
+           debugobjects.xml sh.xml regulator.xml \
            alsa-driver-api.xml writing-an-alsa-driver.xml \
            tracepoint.xml w1.xml \
            writing_musb_glue_layer.xml crypto-API.xml iio.xml
index de955e151af8ee4adc33acbaa22f56b62697ac33..c2a469112c37bbc95d4a58e671eab5344b4aa428 100644 (file)
@@ -1,3 +1 @@
-subdir-y := accounting auxdisplay blackfin \
-       filesystems filesystems ia64 laptops mic misc-devices \
-       networking pcmcia prctl ptp timers vDSO watchdog
+subdir-y :=
diff --git a/Documentation/accounting/Makefile b/Documentation/accounting/Makefile
deleted file mode 100644 (file)
index 7e232cb..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# List of programs to build
-hostprogs-y := getdelays
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_getdelays.o += -I$(objtree)/usr/include
index 8a12f0730c94da018615aebc8e657daf65eadd80..042ea59b5853bdbb328d12591fc124c03f0ebcdd 100644 (file)
@@ -54,9 +54,9 @@ are sent to userspace without requiring a command. If it is the last exiting
 task of a thread group, the per-tgid statistics are also sent. More details
 are given in the taskstats interface description.
 
-The getdelays.c userspace utility in this directory allows simple commands to
-be run and the corresponding delay statistics to be displayed. It also serves
-as an example of using the taskstats interface.
+The getdelays.c userspace utility in tools/accounting directory allows simple
+commands to be run and the corresponding delay statistics to be displayed. It
+also serves as an example of using the taskstats interface.
 
 Usage
 -----
index dea011c8d7c718a8ff7a37dea7d35a81d0f67271..b6e69fd371c4f82c227197f937c8fff9876cec3a 100644 (file)
@@ -8,8 +8,6 @@ Interrupts
        - ARM Interrupt subsystem documentation
 IXP4xx
        - Intel IXP4xx Network processor.
-Makefile
-       - Build sourcefiles as part of the Documentation-build for arm
 Netwinder
        - Netwinder specific documentation
 Porting
diff --git a/Documentation/auxdisplay/Makefile b/Documentation/auxdisplay/Makefile
deleted file mode 100644 (file)
index ada4dac..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# List of programs to build
-hostprogs-y := cfag12864b-example
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_cfag12864b-example.o += -I$(objtree)/usr/include
index eb7be393a51061c6e1cd844dc884edc1a276c53e..12fd51b8de757cac99cf07e330059cf3fb88243f 100644 (file)
@@ -101,5 +101,5 @@ Although the LCD won't get updated until the next refresh time arrives.
 Also, you can mmap the framebuffer: open & mmap, munmap & close...
 which is the best option for most uses.
 
-Check Documentation/auxdisplay/cfag12864b-example.c
+Check samples/auxdisplay/cfag12864b-example.c
 for a real working userspace complete program with usage examples.
index c54fcdd4ae9f68ce6ee439722c0bfac494651406..265a1effebde97cca268e53bb6d49b899dcbe7e0 100644 (file)
@@ -1,10 +1,6 @@
 00-INDEX
        - This file
-Makefile
-       - Makefile for gptimers example file.
 bfin-gpio-notes.txt
        - Notes in developing/using bfin-gpio driver.
 bfin-spi-notes.txt
        - Notes for using bfin spi bus driver.
-gptimers-example.c
-       - gptimers example
diff --git a/Documentation/blackfin/Makefile b/Documentation/blackfin/Makefile
deleted file mode 100644 (file)
index 6782c58..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-ifneq ($(CONFIG_BLACKFIN),)
-ifneq ($(CONFIG_BFIN_GPTIMERS),)
-obj-m := gptimers-example.o
-endif
-endif
diff --git a/Documentation/devicetree/bindings/auxdisplay/img-ascii-lcd.txt b/Documentation/devicetree/bindings/auxdisplay/img-ascii-lcd.txt
new file mode 100644 (file)
index 0000000..b69bb68
--- /dev/null
@@ -0,0 +1,17 @@
+Binding for ASCII LCD displays on Imagination Technologies boards
+
+Required properties:
+- compatible : should be one of:
+    "img,boston-lcd"
+    "mti,malta-lcd"
+    "mti,sead3-lcd"
+
+Required properties for "img,boston-lcd":
+- reg : memory region locating the device registers
+
+Required properties for "mti,malta-lcd" or "mti,sead3-lcd":
+- regmap: phandle of the system controller containing the LCD registers
+- offset: offset in bytes to the LCD registers within the system controller
+
+The layout of the registers & properties of the display are determined
+from the compatible string, making this binding somewhat trivial.
index f31b2ad1552bd425c232c05b8ae3ed431b030260..5fa691e6f6388320acd4199995ef0072e9e70faa 100644 (file)
@@ -32,6 +32,14 @@ wants to support one of the below features, it should adapt the bindings below.
 - clock-frequency
        frequency of bus clock in Hz.
 
+- i2c-bus
+       For I2C adapters that have child nodes that are a mixture of both I2C
+       devices and non-I2C devices, the 'i2c-bus' subnode can be used for
+       populating I2C devices. If the 'i2c-bus' subnode is present, only
+       subnodes of this will be considered as I2C slaves. The properties,
+       '#address-cells' and '#size-cells' must be defined under this subnode
+       if present.
+
 - i2c-scl-falling-time-ns
        Number of nanoseconds the SCL signal takes to fall; t(f) in the I2C
        specification.
index 1416c6a0d2cd8c198944f5c4b2718e27263be44f..fbbad6446741e53fc9a4e65ec74ffd793e2fc44d 100644 (file)
@@ -51,7 +51,6 @@ fsl,sgtl5000          SGTL5000: Ultra Low-Power Audio Codec
 gmt,g751               G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
 infineon,slb9635tt     Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz)
 infineon,slb9645tt     Infineon SLB9645 I2C TPM (new protocol, max 400khz)
-isil,isl12057          Intersil ISL12057 I2C RTC Chip
 isil,isl29028          Intersil ISL29028 Ambient Light and Proximity Sensor
 maxim,ds1050           5 Bit Programmable, Pulse-Width Modulator
 maxim,max1237          Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
index f97993be2dcbdc82dee8866e3510b18f6e121d54..d3b273e4336a72623263d9e62e50a9c39f45c178 100644 (file)
@@ -14,6 +14,7 @@ length of memory mapped region.
 representing a ethernet device.
 - dsaf-handle: phandle, specifies a reference to a node
 representing a dsaf device.
+- node_guid: a number that uniquely identifies a device or component
 - #address-cells: must be 2
 - #size-cells: must be 2
 Optional properties:
@@ -32,6 +33,7 @@ Example:
                        dma-coherent;
                        eth-handle = <&eth2 &eth3 &eth4 &eth5 &eth6 &eth7>;
                        dsaf-handle = <&soc0_dsa>;
+                       node-guid = [00 9A CD 00 00 01 02 03];
                        #address-cells = <2>;
                        #size-cells = <2>;
                        interrupt-parent = <&mbigen_dsa>;
diff --git a/Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt b/Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt
new file mode 100644 (file)
index 0000000..7b8944c
--- /dev/null
@@ -0,0 +1,21 @@
+* MELFAS MIP4 Touchscreen
+
+Required properties:
+- compatible: must be "melfas,mip4_ts"
+- reg: I2C slave address of the chip (0x48 or 0x34)
+- interrupt-parent: interrupt controller to which the chip is connected
+- interrupts: interrupt to which the chip is connected
+
+Optional properties:
+- ce-gpios: GPIO connected to the CE (chip enable) pin of the chip
+
+Example:
+       i2c@00000000 {
+               touchscreen: melfas_mip4@48 {
+                       compatible = "melfas,mip4_ts";
+                       reg = <0x48>;
+                       interrupt-parent = <&gpio>;
+                       interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+                       ce-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
+               };
+       };
index 4a7e030e4f9bae7880650d9fbb3afb79673a4f77..e4e1cd91fb1f2f9701d6df46da1b3461aaaa3a3d 100644 (file)
@@ -2,9 +2,9 @@
 
 Required properties:
 
-- compatible: "brcm,bcm3384", "brcm,bcm33843"
+- compatible: "brcm,bcm3368", "brcm,bcm3384", "brcm,bcm33843"
               "brcm,bcm3384-viper", "brcm,bcm33843-viper"
-              "brcm,bcm6328", "brcm,bcm6358", "brcm,bcm6368",
+              "brcm,bcm6328", "brcm,bcm6358", "brcm,bcm6362", "brcm,bcm6368",
               "brcm,bcm63168", "brcm,bcm63268",
               "brcm,bcm7125", "brcm,bcm7346", "brcm,bcm7358", "brcm,bcm7360",
               "brcm,bcm7362", "brcm,bcm7420", "brcm,bcm7425"
index 5e60ad18f147c2399b418026968068a9ccc7cf0d..2ad18c4ea55c5f022f0181c78896d5a8e33d4e74 100644 (file)
@@ -43,7 +43,9 @@ aspeed,ast2500-pinctrl, aspeed,g5-pinctrl:
 
 GPID0 GPID2 GPIE0 I2C10 I2C11 I2C12 I2C13 I2C14 I2C3 I2C4 I2C5 I2C6 I2C7 I2C8
 I2C9 MAC1LINK MDIO1 MDIO2 OSCCLK PEWAKE PWM0 PWM1 PWM2 PWM3 PWM4 PWM5 PWM6 PWM7
-RGMII1 RGMII2 RMII1 RMII2 SD1 SPI1 TIMER4 TIMER5 TIMER6 TIMER7 TIMER8
+RGMII1 RGMII2 RMII1 RMII2 SD1 SPI1 SPI1DEBUG SPI1PASSTHRU TIMER4 TIMER5 TIMER6
+TIMER7 TIMER8 VGABIOSROM
+
 
 Examples:
 
diff --git a/Documentation/devicetree/bindings/pwm/pwm-meson.txt b/Documentation/devicetree/bindings/pwm/pwm-meson.txt
new file mode 100644 (file)
index 0000000..5376a44
--- /dev/null
@@ -0,0 +1,23 @@
+Amlogic Meson PWM Controller
+============================
+
+Required properties:
+- compatible: Shall contain "amlogic,meson8b-pwm" or "amlogic,meson-gxbb-pwm".
+- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of
+  the cells format.
+
+Optional properties:
+- clocks: Could contain one or two parents clocks phandle for each of the two
+  PWM channels.
+- clock-names: Could contain at least the "clkin0" and/or "clkin1" names.
+
+Example:
+
+       pwm_ab: pwm@8550 {
+               compatible = "amlogic,meson-gxbb-pwm";
+               reg = <0x0 0x08550 0x0 0x10>;
+               #pwm-cells = <3>;
+               status = "disabled";
+               clocks = <&xtal>, <&xtal>;
+               clock-names = "clkin0", "clkin1";
+       }
index f8f59baf6b67eb76c49ea59d10a28975df58d781..6f8af2bcc7b77110298b45c7d178066c44abca7d 100644 (file)
@@ -2,8 +2,9 @@ MediaTek display PWM controller
 
 Required properties:
  - compatible: should be "mediatek,<name>-disp-pwm":
-   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
+   - "mediatek,mt2701-disp-pwm": found on mt2701 SoC.
    - "mediatek,mt6595-disp-pwm": found on mt6595 SoC.
+   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
  - reg: physical base address and length of the controller's registers.
  - #pwm-cells: must be 2. See pwm.txt in this directory for a description of
    the cell format.
index 84d2fb807d3c0c24611fa6ad3ed3d01c6e11f3ae..19fce774cafad1773cf4acc499ae1fff072d4c28 100644 (file)
@@ -13,13 +13,14 @@ Required parameters:
 - pinctrl-0:           List of phandles pointing to pin configuration nodes
                        for PWM module.
                        For Pinctrl properties, please refer to [1].
-- clock-names:                 Set to "pwm".
+- clock-names:                 Valid entries are "pwm" and/or "capture".
 - clocks:              phandle of the clock used by the PWM module.
                        For Clk properties, please refer to [2].
+- interrupts:          IRQ for the Capture device
 
 Optional properties:
-- st,pwm-num-chan:     Number of available channels. If not passed, the driver
-                       will consider single channel by default.
+- st,pwm-num-chan:     Number of available PWM channels.  Default is 0.
+- st,capture-num-chan: Number of available Capture channels.  Default is 0.
 
 [1] Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
 [2] Documentation/devicetree/bindings/clock/clock-bindings.txt
@@ -38,4 +39,5 @@ pwm1: pwm@fe510000 {
        clocks = <&clk_sysin>;
        clock-names = "pwm";
        st,pwm-num-chan = <4>;
+       st,capture-num-chan = <2>;
 };
index cf6068b8e9743ce5bdc7f927c936d7f010ba8e00..f1cbeefb30870c0e5ec5479c9582e88711ed5e21 100644 (file)
@@ -6,6 +6,7 @@ Required properties:
     - "allwinner,sun5i-a10s-pwm"
     - "allwinner,sun5i-a13-pwm"
     - "allwinner,sun7i-a20-pwm"
+    - "allwinner,sun8i-h3-pwm"
   - reg: physical base address and length of the controller's registers
   - #pwm-cells: should be 3. See pwm.txt in this directory for a description of
     the cells format.
index 8e76f26487966b40d3041b948624b91bfacfe883..9882b819f173f3277e55b73cbbcffa4b89f4e1cc 100644 (file)
@@ -11,7 +11,7 @@ Optional properties:
 - trickle-diode-disable : Do not use internal trickle charger diode
        Should be given if internal trickle charger diode should be disabled
 Example:
-       ds1390: rtc@68 {
+       ds1390: rtc@0 {
                compatible = "dallas,ds1390";
                trickle-resistor-ohms = <250>;
                reg = <0>;
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx8900.txt b/Documentation/devicetree/bindings/rtc/epson,rx8900.txt
new file mode 100644 (file)
index 0000000..3f61e51
--- /dev/null
@@ -0,0 +1,22 @@
+Real Time Clock driver for:
+  - Epson RX8900
+  - Micro Crystal rv8803
+
+Required properties:
+- compatible: should be: "microcrystal,rv8803" or "epson,rx8900"
+- reg : the I2C address of the device for I2C
+
+Optional properties:
+- epson,vdet-disable : boolean, if present will disable voltage detector.
+  Should be set if no backup battery is used.
+- trickle-diode-disable : boolean, if present will disable internal trickle
+  charger diode
+
+Example:
+
+       rtc: rtc@32 {
+               compatible = "epson,rx8900"
+               reg = <0x32>;
+               epson,vdet-disable;
+               trickle-diode-disable;
+       };
index bf7d11ae9bea68f107936211d8256203f89a6157..bee41f97044e11606bc451c8fa29b0b49c2b202a 100644 (file)
@@ -18,6 +18,18 @@ Optional properties:
   through pmic_power_en
 - clocks: Any internal or external clocks feeding in to rtc
 - clock-names: Corresponding names of the clocks
+- pinctrl-0: a phandle pointing to the pin settings for the device
+- pinctrl-names: should be "default"
+
+Optional subnodes:
+- generic pinctrl node
+
+Required pinctrl subnodes properties:
+- pins - Names of ext_wakeup pins to configure
+
+Optional pinctrl subnodes properties:
+- input-enable - Enables ext_wakeup
+- ti,active-high - Set input active high (by default active low)
 
 Example:
 
@@ -30,4 +42,13 @@ rtc@1c23000 {
        system-power-controller;
        clocks = <&clk_32k_rtc>, <&clk_32768_ck>;
        clock-names = "ext-clk", "int-clk";
+
+       pinctrl-0 = <&ext_wakeup>;
+       pinctrl-names = "default";
+
+       ext_wakeup: ext-wakeup {
+               pins = "ext_wakeup0";
+               input-enable;
+               ti,active-high;
+       };
 };
diff --git a/Documentation/devicetree/bindings/thermal/max77620_thermal.txt b/Documentation/devicetree/bindings/thermal/max77620_thermal.txt
new file mode 100644 (file)
index 0000000..323a3b3
--- /dev/null
@@ -0,0 +1,70 @@
+Thermal driver for MAX77620 Power management IC from Maxim Semiconductor.
+
+Maxim Semiconductor MAX77620 supports alarm interrupts when its
+die temperature crosses 120C and 140C. These threshold temperatures
+are not configurable. Device does not provide the real temperature
+of die other than just indicating whether temperature is above or
+below threshold level.
+
+Required properties:
+-------------------
+#thermal-sensor-cells: Please refer <devicetree/bindings/thermal/thermal.txt>
+                       for more details.
+                       The value must be 0.
+
+For more details, please refer generic thermal DT binding document
+<devicetree/bindings/thermal/thermal.txt>.
+
+Please refer <devicetree/bindings/mfd/max77620.txt> for mfd DT binding
+document for the MAX77620.
+
+Example:
+--------
+#include <dt-bindings/mfd/max77620.h>
+#include <dt-bindings/thermal/thermal.h>
+...
+
+i2c@7000d000 {
+       spmic: max77620@3c {
+               compatible = "maxim,max77620";
+               :::::
+               #thermal-sensor-cells = <0>;
+               :::
+       };
+};
+
+cool_dev: cool-dev {
+       compatible = "cooling-dev";
+       #cooling-cells = <2>;
+};
+
+thermal-zones {
+       PMIC-Die {
+               polling-delay = <0>;
+               polling-delay-passive = <0>;
+               thermal-sensors = <&spmic>;
+
+               trips {
+                       pmic_die_warn_temp_thresh: hot-die {
+                               temperature = <120000>;
+                               type = "hot";
+                               hysteresis = <0>;
+                       };
+
+                       pmic_die_cirt_temp_thresh: cirtical-die {
+                               temperature = <140000>;
+                               type = "critical";
+                               hysteresis = <0>;
+                       };
+               };
+
+               cooling-maps {
+                       map0 {
+                               trip = <&pmic_die_warn_temp_thresh>;
+                               cooling-device = <&cool_dev THERMAL_NO_LIMIT
+                                                 THERMAL_NO_LIMIT>;
+                               contribution = <100>;
+                       };
+               };
+       };
+};
index 81f9a512bc2a93d079ac6f2c0a5b121e560e9bb8..e2f494d74d8a3fba16c328a3237fd4621ba3caf0 100644 (file)
@@ -8,7 +8,9 @@ apmixedsys register space via AHB bus accesses, so a phandle to the APMIXEDSYS
 is also needed.
 
 Required properties:
-- compatible: "mediatek,mt8173-thermal"
+- compatible:
+  - "mediatek,mt8173-thermal" : For MT8173 family of SoCs
+  - "mediatek,mt2701-thermal" : For MT2701 family of SoCs
 - reg: Address range of the thermal controller
 - interrupts: IRQ for the thermal controller
 - clocks, clock-names: Clocks needed for the thermal controller. required
index edebfa0a985ef03f4557d440a8e41a0c2284e033..b6c0ae53d4dca345f3b751df5b39217497d075fa 100644 (file)
@@ -10,8 +10,14 @@ Required properties :
 - compatible : For Tegra124, must contain "nvidia,tegra124-soctherm".
   For Tegra132, must contain "nvidia,tegra132-soctherm".
   For Tegra210, must contain "nvidia,tegra210-soctherm".
-- reg : Should contain 1 entry:
+- reg : Should contain at least 2 entries for each entry in reg-names:
   - SOCTHERM register set
+  - Tegra CAR register set: Required for Tegra124 and Tegra210.
+  - CCROC register set: Required for Tegra132.
+- reg-names :  Should contain at least 2 entries:
+  - soctherm-reg
+  - car-reg
+  - ccroc-reg
 - interrupts : Defines the interrupt used by SOCTHERM
 - clocks : Must contain an entry for each entry in clock-names.
   See ../clocks/clock-bindings.txt for details.
@@ -25,17 +31,45 @@ Required properties :
 - #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description
     of this property. See <dt-bindings/thermal/tegra124-soctherm.h> for a
     list of valid values when referring to thermal sensors.
+- throttle-cfgs: A sub-node which is a container of configuration for each
+    hardware throttle events. These events can be set as cooling devices.
+  * throttle events: Sub-nodes must be named as "light" or "heavy".
+      Properties:
+      - nvidia,priority: Each throttles has its own throttle settings, so the
+        SW need to set priorities for various throttle, the HW arbiter can select
+        the final throttle settings.
+        Bigger value indicates higher priority, In general, higher priority
+        translates to lower target frequency. SW needs to ensure that critical
+        thermal alarms are given higher priority, and ensure that there is
+        no race if priority of two vectors is set to the same value.
+        The range of this value is 1~100.
+      - nvidia,cpu-throt-percent: This property is for Tegra124 and Tegra210.
+        It is the throttling depth of pulse skippers, it's the percentage
+        throttling.
+      - nvidia,cpu-throt-level: This property is only for Tegra132, it is the
+        level of pulse skippers, which used to throttle clock frequencies. It
+        indicates cpu clock throttling depth, and the depth can be programmed.
+        Must set as following values:
+        TEGRA_SOCTHERM_THROT_LEVEL_LOW, TEGRA_SOCTHERM_THROT_LEVEL_MED
+        TEGRA_SOCTHERM_THROT_LEVEL_HIGH, TEGRA_SOCTHERM_THROT_LEVEL_NONE
+      - #cooling-cells: Should be 1. This cooling device only support on/off state.
+        See ./thermal.txt for a description of this property.
 
 Note:
 - the "critical" type trip points will be set to SOC_THERM hardware as the
 shut down temperature. Once the temperature of this thermal zone is higher
 than it, the system will be shutdown or reset by hardware.
+- the "hot" type trip points will be set to SOC_THERM hardware as the throttle
+temperature. Once the the temperature of this thermal zone is higher
+than it, it will trigger the HW throttle event.
 
 Example :
 
        soctherm@700e2000 {
                compatible = "nvidia,tegra124-soctherm";
-               reg = <0x0 0x700e2000 0x0 0x1000>;
+               reg = <0x0 0x700e2000 0x0 0x600  /* SOC_THERM reg_base */
+                       0x0 0x60006000 0x0 0x400 /* CAR reg_base */
+               reg-names = "soctherm-reg", "car-reg";
                interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&tegra_car TEGRA124_CLK_TSENSOR>,
                        <&tegra_car TEGRA124_CLK_SOC_THERM>;
@@ -44,6 +78,76 @@ Example :
                reset-names = "soctherm";
 
                #thermal-sensor-cells = <1>;
+
+               throttle-cfgs {
+                       /*
+                        * When the "heavy" cooling device triggered,
+                        * the HW will skip cpu clock's pulse in 85% depth
+                        */
+                       throttle_heavy: heavy {
+                               nvidia,priority = <100>;
+                               nvidia,cpu-throt-percent = <85>;
+
+                               #cooling-cells = <1>;
+                       };
+
+                       /*
+                        * When the "light" cooling device triggered,
+                        * the HW will skip cpu clock's pulse in 50% depth
+                        */
+                       throttle_light: light {
+                               nvidia,priority = <80>;
+                               nvidia,cpu-throt-percent = <50>;
+
+                               #cooling-cells = <1>;
+                       };
+
+                       /*
+                        * If these two devices are triggered in same time, the HW throttle
+                        * arbiter will select the highest priority as the final throttle
+                        * settings to skip cpu pulse.
+                        */
+               };
+       };
+
+Example: referring to Tegra132's "reg", "reg-names" and "throttle-cfgs" :
+
+       soctherm@700e2000 {
+               compatible = "nvidia,tegra132-soctherm";
+               reg = <0x0 0x700e2000 0x0 0x600  /* SOC_THERM reg_base */
+                       0x0 0x70040000 0x0 0x200>; /* CCROC reg_base */;
+               reg-names = "soctherm-reg", "ccroc-reg";
+
+               throttle-cfgs {
+                       /*
+                        * When the "heavy" cooling device triggered,
+                        * the HW will skip cpu clock's pulse in HIGH level
+                        */
+                       throttle_heavy: heavy {
+                               nvidia,priority = <100>;
+                               nvidia,cpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_HIGH>;
+
+                               #cooling-cells = <1>;
+                       };
+
+                       /*
+                        * When the "light" cooling device triggered,
+                        * the HW will skip cpu clock's pulse in MED level
+                        */
+                       throttle_light: light {
+                               nvidia,priority = <80>;
+                               nvidia,cpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_MED>;
+
+                               #cooling-cells = <1>;
+                       };
+
+                       /*
+                        * If these two devices are triggered in same time, the HW throttle
+                        * arbiter will select the highest priority as the final throttle
+                        * settings to skip cpu pulse.
+                        */
+
+               };
        };
 
 Example: referring to thermal sensors :
@@ -62,6 +166,19 @@ Example: referring to thermal sensors :
                                        hysteresis = <1000>;
                                        type = "critical";
                                };
+
+                               cpu_throttle_trip: throttle-trip {
+                                       temperature = <100000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&cpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
                        };
                 };
        };
diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
new file mode 100644 (file)
index 0000000..292ed89
--- /dev/null
@@ -0,0 +1,21 @@
+* QCOM SoC Temperature Sensor (TSENS)
+
+Required properties:
+- compatible :
+ - "qcom,msm8916-tsens" : For 8916 Family of SoCs
+ - "qcom,msm8974-tsens" : For 8974 Family of SoCs
+ - "qcom,msm8996-tsens" : For 8996 Family of SoCs
+
+- reg: Address range of the thermal registers
+- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
+- Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify
+nvmem cells
+
+Example:
+tsens: thermal-sensor@900000 {
+               compatible = "qcom,msm8916-tsens";
+               reg = <0x4a8000 0x2000>;
+               nvmem-cells = <&tsens_caldata>, <&tsens_calsel>;
+               nvmem-cell-names = "caldata", "calsel";
+               #thermal-sensor-cells = <1>;
+       };
index 24c6f658bce147162d92f2009c1a4d319c490be3..f0a48ea78659c933839554ca879babb1b621b264 100644 (file)
@@ -163,9 +163,11 @@ maxim      Maxim Integrated Products
 meas   Measurement Specialties
 mediatek       MediaTek Inc.
 melexis        Melexis N.V.
+melfas MELFAS Inc.
 merrii Merrii Technology Co., Ltd.
 micrel Micrel Inc.
 microchip      Microchip Technology Inc.
+microcrystal   Micro Crystal AG
 micron Micron Technology Inc.
 minix  MINIX Technology Ltd.
 mitsubishi     Mitsubishi Electric Corporation
index 6d63782a73786fcb809aafd9b1c32b344447a9c9..c6ae9c9d5e3e2c13a640d48d2af3d79b8aa90388 100644 (file)
@@ -7,6 +7,8 @@ Required properties:
 - reg                  : Physical base address and size
 
 Optional properties:
+- clocks               : Input clock specifier. Refer to common clock
+                         bindings.
 - clock-frequency      : Frequency of clock in Hz
 - xlnx,wdt-enable-once : 0 - Watchdog can be restarted
                          1 - Watchdog can be enabled just once
@@ -17,6 +19,7 @@ Example:
 axi-timebase-wdt@40100000 {
        clock-frequency = <50000000>;
        compatible = "xlnx,xps-timebase-wdt-1.00.a";
+       clocks = <&clkc 15>;
        reg = <0x40100000 0x10000>;
        xlnx,wdt-enable-once = <0x0>;
        xlnx,wdt-interval = <0x1b>;
index 039c5ca45577c3c4a5f00c4279d9b9374d312c92..b949039bc5025741203232852469a57c71dcbd0b 100644 (file)
@@ -9,8 +9,7 @@ functionality.
 
 Required properties
 
-- compatible   : Must be one of: "st,stih407-lpc" "st,stih416-lpc"
-                                 "st,stih415-lpc" "st,stid127-lpc"
+- compatible   : Should be: "st,stih407-lpc"
 - reg          : LPC registers base address + size
 - interrupts    : LPC interrupt line number and associated flags
 - clocks       : Clock used by LPC device (See: ../clock/clock-bindings.txt)
index 9855ad044386891d017469fd39cf85be13bc70f2..4660bf222db17099e221fc37f2d002ab7b45fd8b 100644 (file)
@@ -22,7 +22,7 @@
     |        m68k: | TODO |
     |       metag: | TODO |
     |  microblaze: | TODO |
-    |        mips: | TODO |
+    |        mips: |  ok  |
     |     mn10300: | TODO |
     |       nios2: | TODO |
     |    openrisc: | TODO |
index 9922939e7d99e3dfbcd51d2dad611f745a9a4f94..f66e748fc5e4eb097c10f4ada57c19fb43ccc0e5 100644 (file)
@@ -2,8 +2,6 @@
        - this file (info on some of the filesystems supported by linux).
 Locking
        - info on locking rules as they pertain to Linux VFS.
-Makefile
-       - Makefile for building the filsystems-part of DocBook.
 9p.txt
        - 9p (v9fs) is an implementation of the Plan 9 remote fs protocol.
 adfs.txt
diff --git a/Documentation/filesystems/Makefile b/Documentation/filesystems/Makefile
deleted file mode 100644 (file)
index 883010c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-y := dnotify_test
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
diff --git a/Documentation/ia64/Makefile b/Documentation/ia64/Makefile
deleted file mode 100644 (file)
index d493163..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-y := aliasing-test
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
index d9ccb94fca951d9ac71a4882cde836750e399aae..c53d089455a427d059aacd28c67e32093b590ce5 100644 (file)
@@ -17,6 +17,7 @@ Contents:
    driver-api/index
    media/index
    gpu/index
+   80211/index
 
 Indices and tables
 ==================
index 1fec1135791d98c987105872c63b5e96589633d3..8d1341ccde6498bebc534bcd7c1ef4560560a96f 100644 (file)
@@ -319,3 +319,60 @@ For touchpad packet, the format is:
                otherwise byte 0 bit 4 must be set and byte 0/4/5 are
                in NEW fmt
  F:         Number of fingers - 3, 0 means 3 fingers, 1 means 4 ...
+
+
+ALPS Absolute Mode - Protocol Version 8
+---------------------------------------
+
+Spoken by SS4 (73 03 14) and SS5 (73 03 28) hardware.
+
+The packet type is given by the APD field, bits 4-5 of byte 3.
+
+Touchpad packet (APD = 0x2):
+
+           b7   b6   b5   b4   b3   b2   b1   b0
+ byte 0:  SWM  SWR  SWL    1    1    0    0   X7
+ byte 1:    0   X6   X5   X4   X3   X2   X1   X0
+ byte 2:    0   Y6   Y5   Y4   Y3   Y2   Y1   Y0
+ byte 3:    0  T&P    1    0    1    0    0   Y7
+ byte 4:    0   Z6   Z5   Z4   Z3   Z2   Z1   Z0
+ byte 5:    0    0    0    0    0    0    0    0
+
+SWM, SWR, SWL: Middle, Right, and Left button states
+
+Touchpad 1 Finger packet (APD = 0x0):
+
+           b7   b6   b5   b4   b3   b2   b1   b0
+ byte 0:  SWM  SWR  SWL    1    1   X2   X1   X0
+ byte 1:   X9   X8   X7    1   X6   X5   X4   X3
+ byte 2:    0  X11  X10  LFB   Y3   Y2   Y1   Y0
+ byte 3:   Y5   Y4    0    0    1 TAPF2 TAPF1 TAPF0
+ byte 4:  Zv7  Y11  Y10    1   Y9   Y8   Y7   Y6
+ byte 5:  Zv6  Zv5  Zv4    0  Zv3  Zv2  Zv1  Zv0
+
+TAPF: ???
+LFB:  ???
+
+Touchpad 2 Finger packet (APD = 0x1):
+
+           b7   b6   b5   b4   b3   b2   b1   b0
+ byte 0:  SWM  SWR  SWL    1    1  AX6  AX5  AX4
+ byte 1: AX11 AX10  AX9  AX8  AX7  AZ1  AY4  AZ0
+ byte 2: AY11 AY10  AY9  CONT AY8  AY7  AY6  AY5
+ byte 3:    0    0    0    1    1  BX6  BX5  BX4
+ byte 4: BX11 BX10  BX9  BX8  BX7  BZ1  BY4  BZ0
+ byte 5: BY11 BY10  BY9    0  BY8  BY7  BY5  BY5
+
+CONT: A 3-or-4 Finger packet is to follow
+
+Touchpad 3-or-4 Finger packet (APD = 0x3):
+
+           b7   b6   b5   b4   b3   b2   b1   b0
+ byte 0:  SWM  SWR  SWL    1    1  AX6  AX5  AX4
+ byte 1: AX11 AX10  AX9  AX8  AX7  AZ1  AY4  AZ0
+ byte 2: AY11 AY10  AY9  OVF  AY8  AY7  AY6  AY5
+ byte 3:    0    0    1    1    1  BX6  BX5  BX4
+ byte 4: BX11 BX10  BX9  BX8  BX7  BZ1  BY4  BZ0
+ byte 5: BY11 BY10  BY9    0  BY8  BY7  BY5  BY5
+
+OVF: 5th finger detected
index 385a5ef41c17b9d0023e39d5f0683b6b76199373..9b9c4797fc55653dec668d82ae8c43ccdab58e4b 100644 (file)
@@ -41,6 +41,7 @@ This document describes the Linux kernel Makefiles.
           --- 6.8 Custom kbuild commands
           --- 6.9 Preprocessing linker scripts
           --- 6.10 Generic header files
+          --- 6.11 Post-link pass
 
        === 7 Kbuild syntax for exported headers
                --- 7.1 header-y
@@ -1237,6 +1238,21 @@ When kbuild executes, the following steps are followed (roughly):
        to list the file in the Kbuild file.
        See "7.4 generic-y" for further info on syntax etc.
 
+--- 6.11 Post-link pass
+
+       If the file arch/xxx/Makefile.postlink exists, this makefile
+       will be invoked for post-link objects (vmlinux and modules.ko)
+       for architectures to run post-link passes on. Must also handle
+       the clean target.
+
+       This pass runs after kallsyms generation. If the architecture
+       needs to modify symbol locations, rather than manipulate the
+       kallsyms, it may be easier to add another postlink target for
+       .tmp_vmlinux? targets to be called from link-vmlinux.sh.
+
+       For example, powerpc uses this to check relocation sanity of
+       the linked vmlinux file.
+
 === 7 Kbuild syntax for exported headers
 
 The kernel includes a set of headers that is exported to userspace.
index a1489e14f8eec3eb14081274ac545e21c09d77d5..37babf91f2cb6de20e0b1a66843d1636d65c71fb 100644 (file)
@@ -1511,7 +1511,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
        i8042.nopnp     [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX
                             controllers
        i8042.notimeout [HW] Ignore timeout condition signalled by controller
-       i8042.reset     [HW] Reset the controller during init and cleanup
+       i8042.reset     [HW] Reset the controller during init, cleanup and
+                            suspend-to-ram transitions, only during s2r
+                            transitions, or never reset
+                       Format: { 1 | Y | y | 0 | N | n }
+                       1, Y, y: always reset controller
+                       0, N, n: don't ever reset controller
+                       Default: only on s2r transitions on x86; most other
+                       architectures force reset to be always executed
        i8042.unlock    [HW] Unlock (ignore) the keylock
        i8042.kbdreset  [HW] Reset device connected to KBD port
 
@@ -2470,6 +2477,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
        nfsrootdebug    [NFS] enable nfsroot debugging messages.
                        See Documentation/filesystems/nfs/nfsroot.txt.
 
+       nfs.callback_nr_threads=
+                       [NFSv4] set the total number of threads that the
+                       NFS client will assign to service NFSv4 callback
+                       requests.
+
        nfs.callback_tcpport=
                        [NFS] set the TCP port on which the NFSv4 callback
                        channel should listen.
@@ -2493,6 +2505,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        of returning the full 64-bit number.
                        The default is to return 64-bit inode numbers.
 
+       nfs.max_session_cb_slots=
+                       [NFSv4.1] Sets the maximum number of session
+                       slots the client will assign to the callback
+                       channel. This determines the maximum number of
+                       callbacks the client will process in parallel for
+                       a particular server.
+
        nfs.max_session_slots=
                        [NFSv4.1] Sets the maximum number of session slots
                        the client will attempt to negotiate with the server.
index 979eacae243d1c3acb63c7e65b6d35e6cb1aba67..54bee77fa728576eebeebe71f3896b097bfaa6b7 100644 (file)
@@ -1,8 +1,9 @@
 Linux Kernel Selftests
 
 The kernel contains a set of "self tests" under the tools/testing/selftests/
-directory. These are intended to be small unit tests to exercise individual
-code paths in the kernel.
+directory. These are intended to be small tests to exercise individual code
+paths in the kernel. Tests are intended to be run after building, installing
+and booting a kernel.
 
 On some systems, hot-plug tests could hang forever waiting for cpu and
 memory to be ready to be offlined. A special hot-plug target is created
index 7c0ac2a26b9e7f26add74355d988b0a645413323..86169dc766f7b1e8c4af53cee2ff72d0d8c055a9 100644 (file)
@@ -1,13 +1,9 @@
 00-INDEX
        - This file
-Makefile
-       - Makefile for building dslm example program.
 asus-laptop.txt
        - information on the Asus Laptop Extras driver.
 disk-shock-protection.txt
        - information on hard disk shock protection.
-dslm.c
-       - Simple Disk Sleep Monitor program
 laptop-mode.txt
        - how to conserve battery power using laptop-mode.
 sony-laptop.txt
diff --git a/Documentation/laptops/Makefile b/Documentation/laptops/Makefile
deleted file mode 100644 (file)
index 0abe44f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-y := dslm
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
index 4ebbfc3f1c6ea803b1cb7b5b71c1e98c6d8e3f17..19276f5d195cb75f5cef6a4a554f17e972c5cd55 100644 (file)
@@ -779,4 +779,4 @@ Monitoring tool
 ---------------
 
 Bartek Kania submitted this, it can be used to measure how much time your disk
-spends spun up/down.  See Documentation/laptops/dslm.c
+spends spun up/down.  See tools/laptop/dslm/dslm.c
diff --git a/Documentation/mic/Makefile b/Documentation/mic/Makefile
deleted file mode 100644 (file)
index a191d45..0000000
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := mpssd
diff --git a/Documentation/mic/mpssd/Makefile b/Documentation/mic/mpssd/Makefile
deleted file mode 100644 (file)
index 06871b0..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-ifndef CROSS_COMPILE
-# List of programs to build
-hostprogs-$(CONFIG_X86_64) := mpssd
-
-mpssd-objs := mpssd.o sysfs.o
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS += -I$(objtree)/usr/include -I$(srctree)/tools/include
-
-ifdef DEBUG
-HOSTCFLAGS += -DDEBUG=$(DEBUG)
-endif
-
-HOSTLOADLIBES_mpssd := -lpthread
-
-install:
-       install mpssd /usr/sbin/mpssd
-       install micctrl /usr/sbin/micctrl
-endif
diff --git a/Documentation/misc-devices/Makefile b/Documentation/misc-devices/Makefile
deleted file mode 100644 (file)
index e2b7aa4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := mei
diff --git a/Documentation/misc-devices/mei/Makefile b/Documentation/misc-devices/mei/Makefile
deleted file mode 100644 (file)
index d758047..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-y := mei-amt-version
-HOSTCFLAGS_mei-amt-version.o += -I$(objtree)/usr/include
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
index a7697783ac4c5ddba978e6438c01ca30a0c358c6..c6beb5f1637f9606475bb22746944333e5345b92 100644 (file)
@@ -10,8 +10,6 @@ LICENSE.qlge
        - GPLv2 for QLogic Linux qlge NIC Driver
 LICENSE.qlcnic
        - GPLv2 for QLogic Linux qlcnic NIC Driver
-Makefile
-       - Makefile for docsrc.
 PLIP.txt
        - PLIP: The Parallel Line Internet Protocol device driver
 README.ipw2100
diff --git a/Documentation/networking/Makefile b/Documentation/networking/Makefile
deleted file mode 100644 (file)
index 4c5d7c4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := timestamping
diff --git a/Documentation/networking/timestamping/Makefile b/Documentation/networking/timestamping/Makefile
deleted file mode 100644 (file)
index 8c20dfa..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# To compile, from the source root
-#
-#    make headers_install
-#    make M=documentation
-
-# List of programs to build
-hostprogs-y := hwtstamp_config timestamping txtimestamp
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_timestamping.o += -I$(objtree)/usr/include
-HOSTCFLAGS_txtimestamp.o += -I$(objtree)/usr/include
-HOSTCFLAGS_hwtstamp_config.o += -I$(objtree)/usr/include
diff --git a/Documentation/pcmcia/Makefile b/Documentation/pcmcia/Makefile
deleted file mode 100644 (file)
index 47a8fa1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# List of programs to build
-hostprogs-y := crc32hash
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_crc32hash.o += -I$(objtree)/usr/include
index 199afd100cf27026bf1d918caaeb4d10636262c0..5f3e00ab54c42373882d488765bd1583617041d7 100644 (file)
@@ -27,7 +27,7 @@ pcmcia:m0149cC1ABf06pfn00fn00pa725B842DpbF1EFEE84pc0877B627pd00000000
 The hex value after "pa" is the hash of product ID string 1, after "pb" for
 string 2 and so on.
 
-Alternatively, you can use crc32hash (see Documentation/pcmcia/crc32hash.c)
+Alternatively, you can use crc32hash (see tools/pcmcia/crc32hash.c)
 to determine the crc32 hash.  Simply pass the string you want to evaluate
 as argument to this program, e.g.:
-$ ./crc32hash "Dual Speed"
+$ tools/pcmcia/crc32hash "Dual Speed"
diff --git a/Documentation/prctl/Makefile b/Documentation/prctl/Makefile
deleted file mode 100644 (file)
index 44de308..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-ifndef CROSS_COMPILE
-# List of programs to build
-hostprogs-$(CONFIG_X86) := disable-tsc-ctxt-sw-stress-test disable-tsc-on-off-stress-test disable-tsc-test
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_disable-tsc-ctxt-sw-stress-test.o += -I$(objtree)/usr/include
-HOSTCFLAGS_disable-tsc-on-off-stress-test.o += -I$(objtree)/usr/include
-HOSTCFLAGS_disable-tsc-test.o += -I$(objtree)/usr/include
-endif
diff --git a/Documentation/ptp/Makefile b/Documentation/ptp/Makefile
deleted file mode 100644 (file)
index 293d6c0..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# List of programs to build
-hostprogs-y := testptp
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_testptp.o += -I$(objtree)/usr/include
-HOSTLOADLIBES_testptp := -lrt
index fd880150aeead89920e75758f7194a1ae48eee2d..e2c187947e588d6146c464852e07735966f95145 100644 (file)
@@ -21,16 +21,6 @@ NCR53c400 card, the Trantor T130B in its default configuration:
 The NCR53c400 does not support DMA but it does have Pseudo-DMA which is
 supported by the driver.
 
-If the default configuration does not work for you, you can use the kernel
-command lines (eg using the lilo append command):
-       ncr5380=addr,irq
-       ncr53c400=addr,irq
-       ncr53c400a=addr,irq
-       dtc3181e=addr,irq
-
-The driver does not probe for any addresses or ports other than those in
-the OVERRIDE or given to the kernel as above.
-
 This driver provides some information on what it has detected in
 /proc/scsi/g_NCR5380/x where x is the scsi card number as detected at boot
 time. More info to come in the future.
@@ -38,6 +28,16 @@ time. More info to come in the future.
 This driver works as a module.
 When included as a module, parameters can be passed on the insmod/modprobe
 command line:
+  irq=xx[,...] the interrupt(s)
+  base=xx[,...]        the port or base address(es) (for port or memory mapped, resp.)
+  card=xx[,...]        card type(s):
+               0 = NCR5380,
+               1 = NCR53C400,
+               2 = NCR53C400A,
+               3 = Domex Technology Corp 3181E (DTC3181E)
+               4 = Hewlett Packard C2502
+
+These old-style parameters can support only one card:
   ncr_irq=xx   the interrupt
   ncr_addr=xx  the port or base address (for port or memory
                mapped, resp.)
@@ -46,11 +46,19 @@ command line:
   ncr_53c400a=1 to set up for a NCR53C400A board
   dtc_3181e=1  to set up for a Domex Technology Corp 3181E board
   hp_c2502=1   to set up for a Hewlett Packard C2502 board
+
 e.g.
-modprobe g_NCR5380 ncr_irq=5 ncr_addr=0x350 ncr_5380=1
+OLD: modprobe g_NCR5380 ncr_irq=5 ncr_addr=0x350 ncr_5380=1
+NEW: modprobe g_NCR5380 irq=5 base=0x350 card=0
   for a port mapped NCR5380 board or
-modprobe g_NCR5380 ncr_irq=255 ncr_addr=0xc8000 ncr_53c400=1
-  for a memory mapped NCR53C400 board with interrupts disabled.
+
+OLD: modprobe g_NCR5380 ncr_irq=255 ncr_addr=0xc8000 ncr_53c400=1
+NEW: modprobe g_NCR5380 irq=255 base=0xc8000 card=1
+  for a memory mapped NCR53C400 board with interrupts disabled or
+
+NEW: modprobe g_NCR5380 irq=0,7 base=0x240,0x300 card=3,4
+  for two cards: DTC3181 (in non-PnP mode) at 0x240 with no IRQ
+             and HP C2502 at 0x300 with IRQ 7
 
 (255 should be specified for no or DMA interrupt, 254 to autoprobe for an 
      IRQ line if overridden on the command line.)
index 4644bf0d9832ee47d96a9f4ec923cb31a6208f85..8e4bb17d70ebbf1d01d5960520c7878c22cbef0b 100644 (file)
@@ -1,7 +1,5 @@
 00-INDEX
        - this file.
-Makefile
-       - Makefile for the example sourcefiles.
 butterfly
        - AVR Butterfly SPI driver overview and pin configuration.
 ep93xx_spi
index efc3f3d293c499d479e0c558837157caca5d0352..ef473dc7f55edd445abe23dd7d3866a873d490e1 100644 (file)
@@ -49,6 +49,9 @@ temperature) and throttle appropriate devices.
        .bind: bind the thermal zone device with a thermal cooling device.
        .unbind: unbind the thermal zone device with a thermal cooling device.
        .get_temp: get the current temperature of the thermal zone.
+       .set_trips: set the trip points window. Whenever the current temperature
+                   is updated, the trip points immediately below and above the
+                   current temperature are found.
        .get_mode: get the current mode (enabled/disabled) of the thermal zone.
            - "enabled" means the kernel thermal management is enabled.
            - "disabled" will prevent kernel thermal driver action upon trip points
@@ -95,6 +98,10 @@ temperature) and throttle appropriate devices.
                        get_temp:       a pointer to a function that reads the
                                        sensor temperature. This is mandatory
                                        callback provided by sensor driver.
+                       set_trips:      a pointer to a function that sets a
+                                       temperature window. When this window is
+                                       left the driver must inform the thermal
+                                       core via thermal_zone_device_update.
                        get_trend:      a pointer to a function that reads the
                                        sensor temperature trend.
                        set_emul_temp:  a pointer to a function that sets
@@ -140,6 +147,18 @@ temperature) and throttle appropriate devices.
        Normally this function will not need to be called and the resource
        management code will ensure that the resource is freed.
 
+1.1.7 int thermal_zone_get_slope(struct thermal_zone_device *tz)
+
+       This interface is used to read the slope attribute value
+       for the thermal zone device, which might be useful for platform
+       drivers for temperature calculations.
+
+1.1.8 int thermal_zone_get_offset(struct thermal_zone_device *tz)
+
+       This interface is used to read the offset attribute value
+       for the thermal zone device, which might be useful for platform
+       drivers for temperature calculations.
+
 1.2 thermal cooling device interface
 1.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name,
                void *devdata, struct thermal_cooling_device_ops *)
index ee212a27772f64882b6280f7ef1065a332e5222d..3be05fe0f1f9e57d983042b5b2e4e27811bb5590 100644 (file)
@@ -4,12 +4,8 @@ highres.txt
        - High resolution timers and dynamic ticks design notes
 hpet.txt
        - High Precision Event Timer Driver for Linux
-hpet_example.c
-       - sample hpet timer test program
 hrtimers.txt
        - subsystem for high-resolution kernel timers
-Makefile
-       - Build and link hpet_example
 NO_HZ.txt
        - Summary of the different methods for the scheduler clock-interrupts management.
 timekeeping.txt
diff --git a/Documentation/timers/Makefile b/Documentation/timers/Makefile
deleted file mode 100644 (file)
index 6c09ee6..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-$(CONFIG_X86) := hpet_example
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
index a484d2c109d7ff0689d8997c04ce477ca6e79d1c..895345ec513b5cfe90ac3a2d7c95d355ecce121a 100644 (file)
@@ -25,4 +25,4 @@ arch/x86/kernel/hpet.c.
 
 The driver provides a userspace API which resembles the API found in the
 RTC driver framework.  An example user space program is provided in
-file:Documentation/timers/hpet_example.c
+file:samples/timers/hpet_example.c
diff --git a/Documentation/vDSO/Makefile b/Documentation/vDSO/Makefile
deleted file mode 100644 (file)
index b12e987..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-ifndef CROSS_COMPILE
-# vdso_test won't build for glibc < 2.16, so disable it
-# hostprogs-y := vdso_test
-hostprogs-$(CONFIG_X86) := vdso_standalone_test_x86
-vdso_standalone_test_x86-objs := vdso_standalone_test_x86.o parse_vdso.o
-vdso_test-objs := parse_vdso.o vdso_test.o
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS := -I$(objtree)/usr/include -std=gnu99
-HOSTCFLAGS_vdso_standalone_test_x86.o := -fno-asynchronous-unwind-tables -fno-stack-protector
-HOSTLOADLIBES_vdso_standalone_test_x86 := -nostdlib
-ifeq ($(CONFIG_X86_32),y)
-HOSTLOADLIBES_vdso_standalone_test_x86 += -lgcc_s
-endif
-endif
diff --git a/Documentation/watchdog/Makefile b/Documentation/watchdog/Makefile
deleted file mode 100644 (file)
index 6018f45..0000000
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := src
diff --git a/Documentation/watchdog/src/Makefile b/Documentation/watchdog/src/Makefile
deleted file mode 100644 (file)
index 4a892c3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-y := watchdog-simple watchdog-test
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
index b3a701f48118976a285259b0db73fc9d1a4948fa..0e62ba33b7fbb9507ac415689cae2700a2838b30 100644 (file)
@@ -37,7 +37,7 @@ activates as soon as /dev/watchdog is opened and will reboot unless
 the watchdog is pinged within a certain time, this time is called the
 timeout or margin.  The simplest way to ping the watchdog is to write
 some data to the device.  So a very simple watchdog daemon would look
-like this source file:  see Documentation/watchdog/src/watchdog-simple.c
+like this source file:  see samples/watchdog/watchdog-simple.c
 
 A more advanced driver could for example check that a HTTP server is
 still responding before doing the write call to ping the watchdog.
index 7f31125c123ecefec8f217a9cd8a3e8e5964d7b8..ea277478982f5aef1e122f33f55140e8c5010fc4 100644 (file)
@@ -48,8 +48,10 @@ struct watchdog_device {
        const struct attribute_group **groups;
        const struct watchdog_info *info;
        const struct watchdog_ops *ops;
+       const struct watchdog_governor *gov;
        unsigned int bootstatus;
        unsigned int timeout;
+       unsigned int pretimeout;
        unsigned int min_timeout;
        unsigned int max_timeout;
        unsigned int min_hw_heartbeat_ms;
@@ -74,9 +76,11 @@ It contains following fields:
 * info: a pointer to a watchdog_info structure. This structure gives some
   additional information about the watchdog timer itself. (Like it's unique name)
 * ops: a pointer to the list of watchdog operations that the watchdog supports.
+* gov: a pointer to the assigned watchdog device pretimeout governor or NULL.
 * timeout: the watchdog timer's timeout value (in seconds).
   This is the time after which the system will reboot if user space does
   not send a heartbeat request if WDOG_ACTIVE is set.
+* pretimeout: the watchdog timer's pretimeout value (in seconds).
 * min_timeout: the watchdog timer's minimum timeout value (in seconds).
   If set, the minimum configurable value for 'timeout'.
 * max_timeout: the watchdog timer's maximum timeout value (in seconds),
@@ -121,6 +125,7 @@ struct watchdog_ops {
        int (*ping)(struct watchdog_device *);
        unsigned int (*status)(struct watchdog_device *);
        int (*set_timeout)(struct watchdog_device *, unsigned int);
+       int (*set_pretimeout)(struct watchdog_device *, unsigned int);
        unsigned int (*get_timeleft)(struct watchdog_device *);
        int (*restart)(struct watchdog_device *);
        void (*ref)(struct watchdog_device *) __deprecated;
@@ -188,6 +193,23 @@ they are supported. These optional routines/operations are:
   If set_timeout is not provided but, WDIOF_SETTIMEOUT is set, the watchdog
   infrastructure updates the timeout value of the watchdog_device internally
   to the requested value.
+  If the pretimeout feature is used (WDIOF_PRETIMEOUT), then set_timeout must
+  also take care of checking if pretimeout is still valid and set up the timer
+  accordingly. This can't be done in the core without races, so it is the
+  duty of the driver.
+* set_pretimeout: this routine checks and changes the pretimeout value of
+  the watchdog. It is optional because not all watchdogs support pretimeout
+  notification. The timeout value is not an absolute time, but the number of
+  seconds before the actual timeout would happen. It returns 0 on success,
+  -EINVAL for "parameter out of range" and -EIO for "could not write value to
+  the watchdog". A value of 0 disables pretimeout notification.
+  (Note: the WDIOF_PRETIMEOUT needs to be set in the options field of the
+  watchdog's info structure).
+  If the watchdog driver does not have to perform any action but setting the
+  watchdog_device.pretimeout, this callback can be omitted. That means if
+  set_pretimeout is not provided but WDIOF_PRETIMEOUT is set, the watchdog
+  infrastructure updates the pretimeout value of the watchdog_device internally
+  to the requested value.
 * get_timeleft: this routines returns the time that's left before a reset.
 * restart: this routine restarts the machine. It returns 0 on success or a
   negative errno code for failure.
@@ -268,3 +290,14 @@ User should follow the following guidelines for setting the priority:
 * 128: default restart handler, use if no other handler is expected to be
   available, and/or if restart is sufficient to restart the entire system
 * 255: highest priority, will preempt all other restart handlers
+
+To raise a pretimeout notification, the following function should be used:
+
+void watchdog_notify_pretimeout(struct watchdog_device *wdd)
+
+The function can be called in the interrupt context. If watchdog pretimeout
+governor framework (kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV symbol) is enabled,
+an action is taken by a preconfigured pretimeout governor preassigned to
+the watchdog device. If watchdog pretimeout governor framework is not
+enabled, watchdog_notify_pretimeout() prints a notification message to
+the kernel log buffer.
index 061c2e35384f5eb6354bba48e2da565f18040731..ed2f0b860869b1c5296fe63da31cae90f42e38a2 100644 (file)
@@ -47,4 +47,4 @@ The external event interfaces on the WDT boards are not currently supported.
 Minor numbers are however allocated for it.
 
 
-Example Watchdog Driver:  see Documentation/watchdog/src/watchdog-simple.c
+Example Watchdog Driver:  see samples/watchdog/watchdog-simple.c
index 5e925a25e77d712779b6979be822889b3096ee36..1cd38a7e0064e537a95a9fc7473d28cfdb1822f4 100644 (file)
@@ -316,6 +316,14 @@ W: https://01.org/linux-acpi
 S:     Supported
 F:     drivers/acpi/fan.c
 
+ACPI FOR ARM64 (ACPI/arm64)
+M:     Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+M:     Hanjun Guo <hanjun.guo@linaro.org>
+M:     Sudeep Holla <sudeep.holla@arm.com>
+L:     linux-acpi@vger.kernel.org
+S:     Maintained
+F:     drivers/acpi/arm64
+
 ACPI THERMAL DRIVER
 M:     Zhang Rui <rui.zhang@intel.com>
 L:     linux-acpi@vger.kernel.org
@@ -4775,15 +4783,6 @@ L:       iommu@lists.linux-foundation.org
 S:     Maintained
 F:     drivers/iommu/exynos-iommu.c
 
-EXYNOS MIPI DISPLAY DRIVERS
-M:     Inki Dae <inki.dae@samsung.com>
-M:     Donghwa Lee <dh09.lee@samsung.com>
-M:     Kyungmin Park <kyungmin.park@samsung.com>
-L:     linux-fbdev@vger.kernel.org
-S:     Maintained
-F:     drivers/video/fbdev/exynos/exynos_mipi*
-F:     include/video/exynos_mipi*
-
 EZchip NPS platform support
 M:     Noam Camus <noamc@ezchip.com>
 S:     Supported
@@ -4962,12 +4961,9 @@ F:       drivers/net/wan/dlci.c
 F:     drivers/net/wan/sdla.c
 
 FRAMEBUFFER LAYER
-M:     Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
 M:     Tomi Valkeinen <tomi.valkeinen@ti.com>
 L:     linux-fbdev@vger.kernel.org
-W:     http://linux-fbdev.sourceforge.net/
 Q:     http://patchwork.kernel.org/project/linux-fbdev/list/
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/plagnioj/linux-fbdev.git
 S:     Maintained
 F:     Documentation/fb/
 F:     drivers/video/
@@ -6135,6 +6131,12 @@ M:       Stanislaw Gruszka <stf_xl@wp.pl>
 S:     Maintained
 F:     drivers/usb/atm/ueagle-atm.c
 
+IMGTEC ASCII LCD DRIVER
+M:     Paul Burton <paul.burton@imgtec.com>
+S:     Maintained
+F:     Documentation/devicetree/bindings/auxdisplay/img-ascii-lcd.txt
+F:     drivers/auxdisplay/img-ascii-lcd.c
+
 INA209 HARDWARE MONITOR DRIVER
 M:     Guenter Roeck <linux@roeck-us.net>
 L:     linux-hwmon@vger.kernel.org
@@ -6446,6 +6448,7 @@ F:        include/linux/mei_cl_bus.h
 F:     drivers/misc/mei/*
 F:     drivers/watchdog/mei_wdt.c
 F:     Documentation/misc-devices/mei/*
+F:     samples/mei/*
 
 INTEL MIC DRIVERS (mic)
 M:     Sudeep Dutt <sudeep.dutt@intel.com>
@@ -6632,10 +6635,10 @@ S:      Maintained
 F:     drivers/firmware/iscsi_ibft*
 
 ISCSI
-M:     Mike Christie <michaelc@cs.wisc.edu>
+M:     Lee Duncan <lduncan@suse.com>
+M:     Chris Leech <cleech@redhat.com>
 L:     open-iscsi@googlegroups.com
-W:     www.open-iscsi.org
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mnc/linux-2.6-iscsi.git
+W:     www.open-iscsi.com
 S:     Maintained
 F:     drivers/scsi/*iscsi*
 F:     include/scsi/*iscsi*
@@ -7213,17 +7216,11 @@ F:      drivers/lightnvm/
 F:     include/linux/lightnvm.h
 F:     include/uapi/linux/lightnvm.h
 
-LINUX FOR IBM pSERIES (RS/6000)
-M:     Paul Mackerras <paulus@au.ibm.com>
-W:     http://www.ibm.com/linux/ltc/projects/ppc
-S:     Supported
-F:     arch/powerpc/boot/rs6000.h
-
 LINUX FOR POWERPC (32-BIT AND 64-BIT)
 M:     Benjamin Herrenschmidt <benh@kernel.crashing.org>
 M:     Paul Mackerras <paulus@samba.org>
 M:     Michael Ellerman <mpe@ellerman.id.au>
-W:     http://www.penguinppc.org/
+W:     https://github.com/linuxppc/linux/wiki
 L:     linuxppc-dev@lists.ozlabs.org
 Q:     http://patchwork.ozlabs.org/project/linuxppc-dev/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git
@@ -7238,6 +7235,7 @@ F:        drivers/net/ethernet/ibm/ibmvnic.*
 F:     drivers/pci/hotplug/pnv_php.c
 F:     drivers/pci/hotplug/rpa*
 F:     drivers/scsi/ibmvscsi/
+F:     tools/testing/selftests/powerpc
 N:     opal
 N:     /pmac
 N:     powermac
@@ -7294,9 +7292,8 @@ F:        arch/powerpc/platforms/83xx/
 F:     arch/powerpc/platforms/85xx/
 
 LINUX FOR POWERPC PA SEMI PWRFICIENT
-M:     Olof Johansson <olof@lixom.net>
 L:     linuxppc-dev@lists.ozlabs.org
-S:     Maintained
+S:     Orphan
 F:     arch/powerpc/platforms/pasemi/
 F:     drivers/*/*pasemi*
 F:     drivers/*/*/*pasemi*
@@ -7839,6 +7836,13 @@ F:       Documentation/scsi/megaraid.txt
 F:     drivers/scsi/megaraid.*
 F:     drivers/scsi/megaraid/
 
+MELFAS MIP4 TOUCHSCREEN DRIVER
+M:     Sangwon Jee <jeesw@melfas.com>
+W:     http://www.melfas.com
+S:     Supported
+F:     drivers/input/touchscreen/melfas_mip4.c
+F:     Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt
+
 MELLANOX ETHERNET DRIVER (mlx4_en)
 M:     Tariq Toukan <tariqt@mellanox.com>
 L:     netdev@vger.kernel.org
@@ -9031,15 +9035,13 @@ S:      Maintained
 F:     drivers/net/wireless/intersil/p54/
 
 PA SEMI ETHERNET DRIVER
-M:     Olof Johansson <olof@lixom.net>
 L:     netdev@vger.kernel.org
-S:     Maintained
+S:     Orphan
 F:     drivers/net/ethernet/pasemi/*
 
 PA SEMI SMBUS DRIVER
-M:     Olof Johansson <olof@lixom.net>
 L:     linux-i2c@vger.kernel.org
-S:     Maintained
+S:     Orphan
 F:     drivers/i2c/busses/i2c-pasemi.c
 
 PADATA PARALLEL EXECUTION MECHANISM
@@ -9201,6 +9203,14 @@ S:       Maintained
 F:     Documentation/devicetree/bindings/pci/versatile.txt
 F:     drivers/pci/host/pci-versatile.c
 
+PCI DRIVER FOR ARMADA 8K
+M:     Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+L:     linux-pci@vger.kernel.org
+L:     linux-arm-kernel@lists.infradead.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/pci/pci-armada8k.txt
+F:     drivers/pci/host/pcie-armada8k.c
+
 PCI DRIVER FOR APPLIEDMICRO XGENE
 M:     Tanmay Inamdar <tinamdar@apm.com>
 L:     linux-pci@vger.kernel.org
@@ -9247,6 +9257,7 @@ M:        Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
 L:     linux-pci@vger.kernel.org
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
+F:     Documentation/devicetree/bindings/pci/aardvark-pci.txt
 F:     drivers/pci/host/pci-aardvark.c
 
 PCI DRIVER FOR NVIDIA TEGRA
@@ -9379,6 +9390,7 @@ W:        http://lists.infradead.org/mailman/listinfo/linux-pcmcia
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/brodo/pcmcia.git
 S:     Maintained
 F:     Documentation/pcmcia/
+F:     tools/pcmcia/
 F:     drivers/pcmcia/
 F:     include/pcmcia/
 
index addb235b537c70e3672fcef15c9ad9cc01c6da84..512e47a53e9aebf10b144b888b349df5ede5f8d0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 4
-PATCHLEVEL = 8
+PATCHLEVEL = 9
 SUBLEVEL = 0
-EXTRAVERSION =
+EXTRAVERSION = -rc1
 NAME = Psychotic Stoned Sheep
 
 # *DOCUMENTATION*
@@ -621,6 +621,12 @@ include arch/$(SRCARCH)/Makefile
 
 KBUILD_CFLAGS  += $(call cc-option,-fno-delete-null-pointer-checks,)
 KBUILD_CFLAGS  += $(call cc-disable-warning,maybe-uninitialized,)
+KBUILD_CFLAGS  += $(call cc-disable-warning,frame-address,)
+
+ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
+KBUILD_CFLAGS  += $(call cc-option,-ffunction-sections,)
+KBUILD_CFLAGS  += $(call cc-option,-fdata-sections,)
+endif
 
 ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
 KBUILD_CFLAGS  += -Os
@@ -802,6 +808,10 @@ LDFLAGS_BUILD_ID = $(patsubst -Wl$(comma)%,%,\
 KBUILD_LDFLAGS_MODULE += $(LDFLAGS_BUILD_ID)
 LDFLAGS_vmlinux += $(LDFLAGS_BUILD_ID)
 
+ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
+LDFLAGS_vmlinux        += $(call ld-option, --gc-sections,)
+endif
+
 ifeq ($(CONFIG_STRIP_ASM_SYMS),y)
 LDFLAGS_vmlinux        += $(call ld-option, -X,)
 endif
@@ -926,9 +936,6 @@ vmlinux_prereq: $(vmlinux-deps) FORCE
 ifdef CONFIG_HEADERS_CHECK
        $(Q)$(MAKE) -f $(srctree)/Makefile headers_check
 endif
-ifdef CONFIG_BUILD_DOCSRC
-       $(Q)$(MAKE) $(build)=Documentation
-endif
 ifdef CONFIG_GDB_SCRIPTS
        $(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinux-gdb.py
 endif
@@ -941,9 +948,12 @@ endif
 include/generated/autoksyms.h: FORCE
        $(Q)$(CONFIG_SHELL) $(srctree)/scripts/adjust_autoksyms.sh true
 
-# Final link of vmlinux
-      cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
-quiet_cmd_link-vmlinux = LINK    $@
+ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
+
+# Final link of vmlinux with optional arch pass after final link
+    cmd_link-vmlinux =                                                 \
+       $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) ;       \
+       $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
 
 vmlinux: scripts/link-vmlinux.sh vmlinux_prereq $(vmlinux-deps) FORCE
        +$(call if_changed,link-vmlinux)
@@ -1270,6 +1280,7 @@ $(clean-dirs):
 
 vmlinuxclean:
        $(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-vmlinux.sh clean
+       $(Q)$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) clean)
 
 clean: archclean vmlinuxclean
 
index 180ea33164dc45821a78c08043dc95119c1bdb7e..659bdd079277ebdbec01f8a5c9dd38af1a19c888 100644 (file)
@@ -383,6 +383,24 @@ config GCC_PLUGIN_SANCOV
          gcc-4.5 on). It is based on the commit "Add fuzzing coverage support"
          by Dmitry Vyukov <dvyukov@google.com>.
 
+config GCC_PLUGIN_LATENT_ENTROPY
+       bool "Generate some entropy during boot and runtime"
+       depends on GCC_PLUGINS
+       help
+         By saying Y here the kernel will instrument some kernel code to
+         extract some entropy from both original and artificially created
+         program state.  This will help especially embedded systems where
+         there is little 'natural' source of entropy normally.  The cost
+         is some slowdown of the boot process (about 0.5%) and fork and
+         irq processing.
+
+         Note that entropy extracted this way is not cryptographically
+         secure!
+
+         This plugin was ported from grsecurity/PaX. More information at:
+          * https://grsecurity.net/
+          * https://pax.grsecurity.net/
+
 config HAVE_CC_STACKPROTECTOR
        bool
        help
@@ -450,6 +468,27 @@ config CC_STACKPROTECTOR_STRONG
 
 endchoice
 
+config THIN_ARCHIVES
+       bool
+       help
+         Select this if the architecture wants to use thin archives
+         instead of ld -r to create the built-in.o files.
+
+config LD_DEAD_CODE_DATA_ELIMINATION
+       bool
+       help
+         Select this if the architecture wants to do dead code and
+         data elimination with the linker by compiling with
+         -ffunction-sections -fdata-sections and linking with
+         --gc-sections.
+
+         This requires that the arch annotates or otherwise protects
+         its external entry points from being discarded. Linker scripts
+         must also merge .text.*, .data.*, and .bss.* correctly into
+         output sections. Care must be taken not to pull in unrelated
+         sections (e.g., '.text.init'). Typically '.' in section names
+         is used to distinguish them from label names / C identifiers.
+
 config HAVE_ARCH_WITHIN_STACK_FRAMES
        bool
        help
index ffd9cf5ec8c407c4686c519de89b3e827f1656a2..bf8475ce85ee2ab37a5e69faf86f7acd22ac59dc 100644 (file)
@@ -3,6 +3,7 @@
 generic-y += clkdev.h
 generic-y += cputime.h
 generic-y += exec.h
+generic-y += export.h
 generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += mm-arch-hooks.h
index 466e42e96bfaf29d46567e45268dc4844ac81ac9..94f587535deed9b48ddfd8c6e343077533aaea34 100644 (file)
@@ -396,11 +396,12 @@ copy_to_user(void __user *to, const void *from, long n)
 extern inline long
 copy_from_user(void *to, const void __user *from, long n)
 {
+       long res = n;
        if (likely(__access_ok((unsigned long)from, n, get_fs())))
-               n = __copy_tofrom_user_nocheck(to, (__force void *)from, n);
-       else
-               memset(to, 0, n);
-       return n;
+               res = __copy_from_user_inatomic(to, from, n);
+       if (unlikely(res))
+               memset(to + (n - res), 0, res);
+       return res;
 }
 
 extern void __do_clear_user(void);
index 3ecac0106c8a1aa1e2fb85b5b868cf9e73eb1091..8ce13d7a2ad389cb26d356b2954e7034326203ac 100644 (file)
@@ -8,7 +8,7 @@ ccflags-y       := -Wno-sign-compare
 
 obj-y    := entry.o traps.o process.o osf_sys.o irq.o \
            irq_alpha.o signal.o setup.o ptrace.o time.o \
-           alpha_ksyms.o systbls.o err_common.o io.o
+           systbls.o err_common.o io.o
 
 obj-$(CONFIG_VGA_HOSE) += console.o
 obj-$(CONFIG_SMP)      += smp.o
diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c
deleted file mode 100644 (file)
index f4c7ab6..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * linux/arch/alpha/kernel/alpha_ksyms.c
- *
- * Export the alpha-specific functions that are needed for loadable
- * modules.
- */
-
-#include <linux/module.h>
-#include <asm/console.h>
-#include <asm/uaccess.h>
-#include <asm/checksum.h>
-#include <asm/fpu.h>
-#include <asm/machvec.h>
-
-#include <linux/syscalls.h>
-
-/* these are C runtime functions with special calling conventions: */
-extern void __divl (void);
-extern void __reml (void);
-extern void __divq (void);
-extern void __remq (void);
-extern void __divlu (void);
-extern void __remlu (void);
-extern void __divqu (void);
-extern void __remqu (void);
-
-EXPORT_SYMBOL(alpha_mv);
-EXPORT_SYMBOL(callback_getenv);
-EXPORT_SYMBOL(callback_setenv);
-EXPORT_SYMBOL(callback_save_env);
-
-/* platform dependent support */
-EXPORT_SYMBOL(strcat);
-EXPORT_SYMBOL(strcpy);
-EXPORT_SYMBOL(strlen);
-EXPORT_SYMBOL(strncpy);
-EXPORT_SYMBOL(strncat);
-EXPORT_SYMBOL(strchr);
-EXPORT_SYMBOL(strrchr);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(__memcpy);
-EXPORT_SYMBOL(__memset);
-EXPORT_SYMBOL(___memset);
-EXPORT_SYMBOL(__memsetw);
-EXPORT_SYMBOL(__constant_c_memset);
-EXPORT_SYMBOL(copy_page);
-EXPORT_SYMBOL(clear_page);
-
-EXPORT_SYMBOL(alpha_read_fp_reg);
-EXPORT_SYMBOL(alpha_read_fp_reg_s);
-EXPORT_SYMBOL(alpha_write_fp_reg);
-EXPORT_SYMBOL(alpha_write_fp_reg_s);
-
-/* Networking helper routines. */
-EXPORT_SYMBOL(csum_tcpudp_magic);
-EXPORT_SYMBOL(ip_compute_csum);
-EXPORT_SYMBOL(ip_fast_csum);
-EXPORT_SYMBOL(csum_partial_copy_nocheck);
-EXPORT_SYMBOL(csum_partial_copy_from_user);
-EXPORT_SYMBOL(csum_ipv6_magic);
-
-#ifdef CONFIG_MATHEMU_MODULE
-extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long);
-extern long (*alpha_fp_emul) (unsigned long pc);
-EXPORT_SYMBOL(alpha_fp_emul_imprecise);
-EXPORT_SYMBOL(alpha_fp_emul);
-#endif
-
-/*
- * The following are specially called from the uaccess assembly stubs.
- */
-EXPORT_SYMBOL(__copy_user);
-EXPORT_SYMBOL(__do_clear_user);
-
-/* 
- * SMP-specific symbols.
- */
-
-#ifdef CONFIG_SMP
-EXPORT_SYMBOL(_atomic_dec_and_lock);
-#endif /* CONFIG_SMP */
-
-/*
- * The following are special because they're not called
- * explicitly (the C compiler or assembler generates them in
- * response to division operations).  Fortunately, their
- * interface isn't gonna change any time soon now, so it's OK
- * to leave it out of version control.
- */
-# undef memcpy
-# undef memset
-EXPORT_SYMBOL(__divl);
-EXPORT_SYMBOL(__divlu);
-EXPORT_SYMBOL(__divq);
-EXPORT_SYMBOL(__divqu);
-EXPORT_SYMBOL(__reml);
-EXPORT_SYMBOL(__remlu);
-EXPORT_SYMBOL(__remq);
-EXPORT_SYMBOL(__remqu);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memchr);
index d3398f6ab74c2d0b80c538a1181d03f47fbed501..b7d69604b6d2e522b8aabc0e904c0ab78b0e637a 100644 (file)
    else beforehand.  Fine.  We'll do it ourselves.  */
 #if 0
 #define ALIAS_MV(system) \
-  struct alpha_machine_vector alpha_mv __attribute__((alias(#system "_mv")));
+  struct alpha_machine_vector alpha_mv __attribute__((alias(#system "_mv"))); \
+  EXPORT_SYMBOL(alpha_mv);
 #else
 #define ALIAS_MV(system) \
-  asm(".global alpha_mv\nalpha_mv = " #system "_mv");
+  asm(".global alpha_mv\nalpha_mv = " #system "_mv"); \
+  EXPORT_SYMBOL(alpha_mv);
 #endif
 #endif /* GENERIC */
index d9ee81769899fb8652046857d2fa3eaabd1bacc1..940dfb4065910822d42f3d11c02ba2305f25b02a 100644 (file)
@@ -157,14 +157,16 @@ put_reg(struct task_struct *task, unsigned long regno, unsigned long data)
 static inline int
 read_int(struct task_struct *task, unsigned long addr, int * data)
 {
-       int copied = access_process_vm(task, addr, data, sizeof(int), 0);
+       int copied = access_process_vm(task, addr, data, sizeof(int),
+                       FOLL_FORCE);
        return (copied == sizeof(int)) ? 0 : -EIO;
 }
 
 static inline int
 write_int(struct task_struct *task, unsigned long addr, int data)
 {
-       int copied = access_process_vm(task, addr, &data, sizeof(int), 1);
+       int copied = access_process_vm(task, addr, &data, sizeof(int),
+                       FOLL_FORCE | FOLL_WRITE);
        return (copied == sizeof(int)) ? 0 : -EIO;
 }
 
@@ -281,7 +283,8 @@ long arch_ptrace(struct task_struct *child, long request,
        /* When I and D space are separate, these will need to be fixed.  */
        case PTRACE_PEEKTEXT: /* read word at location addr. */
        case PTRACE_PEEKDATA:
-               copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+               copied = access_process_vm(child, addr, &tmp, sizeof(tmp),
+                               FOLL_FORCE);
                ret = -EIO;
                if (copied != sizeof(tmp))
                        break;
index b20af76f12c1dbf548a27210fdbb196a2c77496d..4811e54069fcfbb733185d4004e24efa814b9165 100644 (file)
@@ -115,6 +115,7 @@ unsigned long alpha_agpgart_size = DEFAULT_AGP_APER_SIZE;
 
 #ifdef CONFIG_ALPHA_GENERIC
 struct alpha_machine_vector alpha_mv;
+EXPORT_SYMBOL(alpha_mv);
 #endif
 
 #ifndef alpha_using_srm
index 8804bec2c6448e15e5f34be99b25e9bef8518a15..6093addc931a5bf3dec4d7c13f6cf84c88417e39 100644 (file)
@@ -3,6 +3,7 @@
  */
 
 #include <asm/console.h>
+#include <asm/export.h>
 
 .text
 #define HWRPB_CRB_OFFSET 0xc0
@@ -92,6 +93,10 @@ CALLBACK(reset_env, CCB_RESET_ENV, 4)
 CALLBACK(save_env, CCB_SAVE_ENV, 1)
 CALLBACK(pswitch, CCB_PSWITCH, 3)
 CALLBACK(bios_emul, CCB_BIOS_EMUL, 5)
+
+EXPORT_SYMBOL(callback_getenv)
+EXPORT_SYMBOL(callback_setenv)
+EXPORT_SYMBOL(callback_save_env)
        
 .data
 __alpha_using_srm:             # For use by bootpheader
index 377f9e34eb9709631e50aa20ce87417ebdeac3a8..b57f8007db14f78565200134f6f519165d3970ea 100644 (file)
@@ -48,6 +48,7 @@ __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
                (__force u64)saddr + (__force u64)daddr +
                (__force u64)sum + ((len + proto) << 8));
 }
+EXPORT_SYMBOL(csum_tcpudp_magic);
 
 __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
                          __u32 len, __u8 proto, __wsum sum)
@@ -144,6 +145,7 @@ __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
 {
        return (__force __sum16)~do_csum(iph,ihl*4);
 }
+EXPORT_SYMBOL(ip_fast_csum);
 
 /*
  * computes the checksum of a memory block at buff, length len,
@@ -178,3 +180,4 @@ __sum16 ip_compute_csum(const void *buff, int len)
 {
        return (__force __sum16)~from64to16(do_csum(buff,len));
 }
+EXPORT_SYMBOL(ip_compute_csum);
index a221ae266e29098ab55cd72fb42ede2ff11588bf..263d7393c0e7a83fd15e570bb171bc726b55afbc 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Zero an entire page.
  */
-
+#include <asm/export.h>
        .text
        .align 4
        .global clear_page
@@ -37,3 +37,4 @@ clear_page:
        nop
 
        .end clear_page
+       EXPORT_SYMBOL(clear_page)
index 8860316c195769ebdf907d590aee0c19555ac956..bf5b931866ba1ed069758cfa95f01f35e18b6b02 100644 (file)
@@ -24,6 +24,7 @@
  * Clobbers:
  *     $1,$2,$3,$4,$5,$6
  */
+#include <asm/export.h>
 
 /* Allow an exception for an insn; exit if we get one.  */
 #define EX(x,y...)                     \
@@ -111,3 +112,4 @@ $exception:
        ret     $31, ($28), 1   # .. e1 :
 
        .end __do_clear_user
+       EXPORT_SYMBOL(__do_clear_user)
index 9f3b97459cc64fb5eb70ec7b15ded1a1e97a0245..2ee0bd0508c5943aa8eb64e074b6f5bb86de3920 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copy an entire page.
  */
-
+#include <asm/export.h>
        .text
        .align 4
        .global copy_page
@@ -47,3 +47,4 @@ copy_page:
        nop
 
        .end copy_page
+       EXPORT_SYMBOL(copy_page)
index 6f3fab9eb434444bf58cd4ed528b1392c3c7cf53..509f62b6531102b539e61f11519ac4d49da02fe9 100644 (file)
@@ -26,6 +26,8 @@
  *     $1,$2,$3,$4,$5,$6,$7
  */
 
+#include <asm/export.h>
+
 /* Allow an exception for an insn; exit if we get one.  */
 #define EXI(x,y...)                    \
        99: x,##y;                      \
@@ -124,22 +126,9 @@ $65:
        bis $31,$31,$0
 $41:
 $35:
-$exitout:
-       ret $31,($28),1
-
 $exitin:
-       /* A stupid byte-by-byte zeroing of the rest of the output
-          buffer.  This cures security holes by never leaving 
-          random kernel data around to be copied elsewhere.  */
-
-       mov $0,$1
-$101:
-       EXO ( ldq_u $2,0($6) )
-       subq $1,1,$1
-       mskbl $2,$6,$2
-       EXO ( stq_u $2,0($6) )
-       addq $6,1,$6
-       bgt $1,$101
+$exitout:
        ret $31,($28),1
 
        .end __copy_user
+EXPORT_SYMBOL(__copy_user)
index 2c2acb96deb682de173c0a0b3983f92977d3ef8f..e74b4544b0cce0962dd9715f063224a89ed066d4 100644 (file)
@@ -12,6 +12,7 @@
  * added by Ivan Kokshaysky <ink@jurassic.park.msu.ru>
  */
 
+#include <asm/export.h>
        .globl csum_ipv6_magic
        .align 4
        .ent csum_ipv6_magic
@@ -113,3 +114,4 @@ csum_ipv6_magic:
        ret                     # .. e1 :
 
        .end csum_ipv6_magic
+       EXPORT_SYMBOL(csum_ipv6_magic)
index 5675dca8dbb1412e0b2fcea1c748ec9d757a3e8c..b4ff3b683bcd57802c8c422708c3c712098c4cc7 100644 (file)
@@ -374,6 +374,7 @@ csum_partial_copy_from_user(const void __user *src, void *dst, int len,
        }
        return (__force __wsum)checksum;
 }
+EXPORT_SYMBOL(csum_partial_copy_from_user);
 
 __wsum
 csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
@@ -386,3 +387,4 @@ csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
        set_fs(oldfs);
        return checksum;
 }
+EXPORT_SYMBOL(csum_partial_copy_nocheck);
index f9f5fe830e9f9c8912daaf830c4a4b54454ea43c..4221b40167eed5b85284e6d9bc831cdc31094666 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/spinlock.h>
 #include <linux/atomic.h>
+#include <linux/export.h>
 
   asm (".text                                  \n\
        .global _atomic_dec_and_lock            \n\
@@ -39,3 +40,4 @@ static int __used atomic_dec_and_lock_1(atomic_t *atomic, spinlock_t *lock)
        spin_unlock(lock);
        return 0;
 }
+EXPORT_SYMBOL(_atomic_dec_and_lock);
index 2d1a0484a99e009e3e198f47f77b06194b50006d..1e33bd1276213493b44c37acb877de57870cb445 100644 (file)
@@ -45,6 +45,7 @@
  *     $28 - compare status
  */
 
+#include <asm/export.h>
 #define halt .long 0
 
 /*
@@ -151,6 +152,7 @@ ufunction:
        addq    $30,STACK,$30
        ret     $31,($23),1
        .end    ufunction
+EXPORT_SYMBOL(ufunction)
 
 /*
  * Uhh.. Ugly signed division. I'd rather not have it at all, but
@@ -193,3 +195,4 @@ sfunction:
        addq    $30,STACK,$30
        ret     $31,($23),1
        .end    sfunction
+EXPORT_SYMBOL(sfunction)
index adf4f7be0e2b6d6792e420e3b40af21ff8f449ca..abe99e69a1945a2bac7ce4062bd43a7503c52907 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Zero an entire page.
  */
-
+#include <asm/export.h>
         .text
         .align 4
         .global clear_page
@@ -52,3 +52,4 @@ clear_page:
        nop
 
        .end clear_page
+       EXPORT_SYMBOL(clear_page)
index 4f42a16b7f53d18cfc08f076d540011ad6ca5215..05bef6b505984465699627db5aeec012714893f4 100644 (file)
@@ -43,6 +43,7 @@
  *     want to leave a hole (and we also want to avoid repeating lots of work)
  */
 
+#include <asm/export.h>
 /* Allow an exception for an insn; exit if we get one.  */
 #define EX(x,y...)                     \
        99: x,##y;                      \
@@ -222,4 +223,4 @@ $exception:                 # Destination for exception recovery(?)
        nop                     # .. E  .. ..   :
        ret     $31, ($28), 1   # L0 .. .. ..   : L U L U
        .end __do_clear_user
-
+       EXPORT_SYMBOL(__do_clear_user)
index b789db19275443d092494ea80a41be772dedb96a..77935061bddbb652c746938de8e8875239330cab 100644 (file)
@@ -56,7 +56,7 @@
    destination pages are in the dcache, but it is my guess that this is
    less important than the dcache miss case.  */
 
-
+#include <asm/export.h>
        .text
        .align 4
        .global copy_page
@@ -201,3 +201,4 @@ copy_page:
        nop
 
        .end copy_page
+       EXPORT_SYMBOL(copy_page)
index db42ffe9c350b6c9854470b028f6b515b028c0f6..be720b518af9e6500ce7545887a6bf6a9cb715d3 100644 (file)
@@ -37,6 +37,7 @@
  *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
  */
 
+#include <asm/export.h>
 /* Allow an exception for an insn; exit if we get one.  */
 #define EXI(x,y...)                    \
        99: x,##y;                      \
@@ -227,33 +228,12 @@ $dirtyentry:
        bgt $0,$onebyteloop     # U  .. .. ..   : U L U L
 
 $zerolength:
+$exitin:
 $exitout:                      # Destination for exception recovery(?)
        nop                     # .. .. .. E
        nop                     # .. .. E  ..
        nop                     # .. E  .. ..
        ret $31,($28),1         # L0 .. .. ..   : L U L U
 
-$exitin:
-
-       /* A stupid byte-by-byte zeroing of the rest of the output
-          buffer.  This cures security holes by never leaving 
-          random kernel data around to be copied elsewhere.  */
-
-       nop
-       nop
-       nop
-       mov     $0,$1
-
-$101:
-       EXO ( stb $31,0($6) )   # L
-       subq $1,1,$1            # E
-       addq $6,1,$6            # E
-       bgt $1,$101             # U
-
-       nop
-       nop
-       nop
-       ret $31,($28),1         # L0
-
        .end __copy_user
-
+       EXPORT_SYMBOL(__copy_user)
index fc0bc399f872db671313f07e78ac8d20f5ff6637..de62627ac4fe1dce24f68cf94df29afb9099a35d 100644 (file)
@@ -52,6 +52,7 @@
  * may cause additional delay in rare cases (load-load replay traps).
  */
 
+#include <asm/export.h>
        .globl csum_ipv6_magic
        .align 4
        .ent csum_ipv6_magic
@@ -148,3 +149,4 @@ csum_ipv6_magic:
        ret                     # L0 : L U L U
 
        .end csum_ipv6_magic
+       EXPORT_SYMBOL(csum_ipv6_magic)
index 2a82b9be93fa290fdb6e859019db24839a55af18..d18dc0e96e3d7987f0d2b977406a36ef4889b8d7 100644 (file)
@@ -55,6 +55,7 @@
  * Try not to change the actual algorithm if possible for consistency.
  */
 
+#include <asm/export.h>
 #define halt .long 0
 
 /*
@@ -205,6 +206,7 @@ ufunction:
        addq    $30,STACK,$30           # E :
        ret     $31,($23),1             # L0 : L U U L
        .end    ufunction
+EXPORT_SYMBOL(ufunction)
 
 /*
  * Uhh.. Ugly signed division. I'd rather not have it at all, but
@@ -257,3 +259,4 @@ sfunction:
        addq    $30,STACK,$30           # E :
        ret     $31,($23),1             # L0 : L U U L
        .end    sfunction
+EXPORT_SYMBOL(sfunction)
index 1a5f71b9d8b10286f324b2e13e9be79ddb1d4abb..419adc53ccb4e599217aedd3bdb9a6af43bab400 100644 (file)
@@ -27,7 +27,7 @@
  *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
  * Try not to change the actual algorithm if possible for consistency.
  */
-
+#include <asm/export.h>
         .set noreorder
         .set noat
 
@@ -189,3 +189,4 @@ $not_found:
        ret                     # L0 :
 
         .end memchr
+       EXPORT_SYMBOL(memchr)
index 52b37b0f2af5152177cd4a58fa815491070a4afc..b19798b2efc09a30fce801eda1587e817fcb434b 100644 (file)
@@ -19,7 +19,7 @@
  * Temp usage notes:
  *     $1,$2,          - scratch
  */
-
+#include <asm/export.h>
        .set noreorder
        .set noat
 
@@ -242,6 +242,7 @@ $nomoredata:
        nop                             # E :
 
        .end memcpy
+       EXPORT_SYMBOL(memcpy)
 
 /* For backwards module compatibility.  */
 __memcpy = memcpy
index 356bb2fdd70567721023b8e0a3fc1d59f2f5d981..fed21c6893e8e7c295fcaeac56d0b70bb6051133 100644 (file)
@@ -26,7 +26,7 @@
  * as fixes will need to be made in multiple places.  The performance gain
  * is worth it.
  */
-
+#include <asm/export.h>
        .set noat
        .set noreorder
 .text
@@ -229,6 +229,7 @@ end_b:
        nop
        ret $31,($26),1         # L0 :
        .end ___memset
+       EXPORT_SYMBOL(___memset)
 
        /*
         * This is the original body of code, prior to replication and
@@ -406,6 +407,7 @@ end:
        nop
        ret $31,($26),1         # L0 :
        .end __constant_c_memset
+       EXPORT_SYMBOL(__constant_c_memset)
 
        /*
         * This is a replicant of the __constant_c_memset code, rescheduled
@@ -594,6 +596,9 @@ end_w:
        ret $31,($26),1         # L0 :
 
        .end __memsetw
+       EXPORT_SYMBOL(__memsetw)
 
 memset = ___memset
 __memset = ___memset
+       EXPORT_SYMBOL(memset)
+       EXPORT_SYMBOL(__memset)
index c426fe3ed72f4e193a6f7d8b7ec78831c64af203..b69f60419be1bc940c81dac67fdd78b1932c8fb9 100644 (file)
@@ -19,7 +19,7 @@
  * string once.
  */
 
-
+#include <asm/export.h>
        .text
 
        .align 4
@@ -52,3 +52,4 @@ $found:       cttz    $2, $3          # U0 :
        br      __stxcpy        # L0 :
 
        .end strcat
+       EXPORT_SYMBOL(strcat)
index fbb7b4ffade9e596272c1c4ede40689d7bf73a92..ea8f2f35db9cef2487524b93038885ed9b71ce94 100644 (file)
@@ -15,7 +15,7 @@
  *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
  * Try not to change the actual algorithm if possible for consistency.
  */
-
+#include <asm/export.h>
 #include <asm/regdef.h>
 
        .set noreorder
@@ -86,3 +86,4 @@ $found:       negq    t0, t1          # E : clear all but least set bit
        ret                     # L0 :
 
        .end strchr
+       EXPORT_SYMBOL(strchr)
index 503928072523e745b3b62934e52301b1fee2d327..736fd41884a8c11ba44b5b057f23c46e4908c521 100644 (file)
@@ -17,7 +17,7 @@
  *     U       - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
  *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
  */
-
+#include <asm/export.h>
        .set noreorder
        .set noat
 
@@ -47,3 +47,4 @@ $found:
        ret     $31, ($26)      # L0 :
 
        .end    strlen
+       EXPORT_SYMBOL(strlen)
index 4ae716cd2bfbf8e8f6c8b194531efac50f88ec7c..cd35cbade73ae8f46fcf7efff8763646e92efbf4 100644 (file)
@@ -20,7 +20,7 @@
  * Try not to change the actual algorithm if possible for consistency.
  */
 
-
+#include <asm/export.h>
        .text
 
        .align 4
@@ -92,3 +92,4 @@ $zerocount:
        ret                     # L0 :
 
        .end strncat
+       EXPORT_SYMBOL(strncat)
index dd0d8c6b9f59ffdf92d8a0bfe40aa3fdd0bbe29d..747455f0328cf35bfaeac86ac56101d70e841016 100644 (file)
@@ -18,7 +18,7 @@
  *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
  */
 
-
+#include <asm/export.h>
 #include <asm/regdef.h>
 
        .set noreorder
@@ -107,3 +107,4 @@ $eos:
        nop
 
        .end strrchr
+       EXPORT_SYMBOL(strrchr)
index 05017ba34c3cc40e22a97b4de62bdc0f363901de..4aa6dbfa14eecce36fcafcf800e79c4f0a93ee84 100644 (file)
@@ -4,6 +4,9 @@
  * (C) Copyright 1998 Linus Torvalds
  */
 
+#include <linux/compiler.h>
+#include <linux/export.h>
+
 #if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67)
 #define STT(reg,val)  asm volatile ("ftoit $f"#reg",%0" : "=r"(val));
 #else
@@ -52,6 +55,7 @@ alpha_read_fp_reg (unsigned long reg)
        }
        return val;
 }
+EXPORT_SYMBOL(alpha_read_fp_reg);
 
 #if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67)
 #define LDT(reg,val)  asm volatile ("itoft %0,$f"#reg : : "r"(val));
@@ -97,6 +101,7 @@ alpha_write_fp_reg (unsigned long reg, unsigned long val)
              case 31: LDT(31, val); break;
        }
 }
+EXPORT_SYMBOL(alpha_write_fp_reg);
 
 #if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67)
 #define STS(reg,val)  asm volatile ("ftois $f"#reg",%0" : "=r"(val));
@@ -146,6 +151,7 @@ alpha_read_fp_reg_s (unsigned long reg)
        }
        return val;
 }
+EXPORT_SYMBOL(alpha_read_fp_reg_s);
 
 #if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67)
 #define LDS(reg,val)  asm volatile ("itofs %0,$f"#reg : : "r"(val));
@@ -191,3 +197,4 @@ alpha_write_fp_reg_s (unsigned long reg, unsigned long val)
              case 31: LDS(31, val); break;
        }
 }
+EXPORT_SYMBOL(alpha_write_fp_reg_s);
index 14427eeb555e6d8f80bc863e6e5f50c6c7024a87..c13d3eca2e0592736dd17112ff836dcac5c0b7cb 100644 (file)
@@ -31,7 +31,7 @@ For correctness consider that:
       - only minimum number of quadwords may be accessed
       - the third argument is an unsigned long
 */
-
+#include <asm/export.h>
         .set noreorder
         .set noat
 
@@ -162,3 +162,4 @@ $not_found:
        ret                     # .. e1 :
 
         .end memchr
+       EXPORT_SYMBOL(memchr)
index 64083fc732389419aa55e499532a278af4f8adab..57d9291ad172c6a2660104a6af6d4392efac1efb 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <linux/types.h>
+#include <linux/export.h>
 
 /*
  * This should be done in one go with ldq_u*2/mask/stq_u. Do it
@@ -158,6 +159,4 @@ void * memcpy(void * dest, const void *src, size_t n)
        __memcpy_unaligned_up ((unsigned long) dest, (unsigned long) src, n);
        return dest;
 }
-
-/* For backward modules compatibility, define __memcpy.  */
-asm("__memcpy = memcpy; .globl __memcpy");
+EXPORT_SYMBOL(memcpy);
index eb3b6e02242f4b91cffe943b105a04f22f1df249..6872c85cb5e54d35125ebb8bc61b47fa26f33785 100644 (file)
@@ -6,7 +6,7 @@
  * This is hand-massaged output from the original memcpy.c.  We defer to
  * memcpy whenever possible; the backwards copy loops are not unrolled.
  */
-        
+#include <asm/export.h>        
        .set noat
        .set noreorder
        .text
@@ -179,3 +179,4 @@ $egress:
        nop
 
        .end memmove
+       EXPORT_SYMBOL(memmove)
index 76ccc6d1f364d67ca8c03c859171f113da5e23ce..89a26f5e89de3db904cbfabb1ff735464d6ca3cb 100644 (file)
@@ -13,7 +13,7 @@
  * The scheduling comments are according to the EV5 documentation (and done by 
  * hand, so they might well be incorrect, please do tell me about it..)
  */
-
+#include <asm/export.h>
        .set noat
        .set noreorder
 .text
@@ -106,6 +106,8 @@ within_one_quad:
 end:
        ret $31,($26),1         /* E1 */
        .end ___memset
+EXPORT_SYMBOL(___memset)
+EXPORT_SYMBOL(__constant_c_memset)
 
        .align 5
        .ent __memsetw
@@ -122,6 +124,9 @@ __memsetw:
        br __constant_c_memset  /* .. E1 */
 
        .end __memsetw
+EXPORT_SYMBOL(__memsetw)
 
 memset = ___memset
 __memset = ___memset
+       EXPORT_SYMBOL(memset)
+       EXPORT_SYMBOL(__memset)
index 393f50384878fd0ed7dc1ef786bb5c1bed612678..249837b03d4b9958d9131ca1cd2a9ab8c02f75a7 100644 (file)
@@ -4,6 +4,7 @@
  *
  * Append a null-terminated string from SRC to DST.
  */
+#include <asm/export.h>
 
        .text
 
@@ -50,3 +51,4 @@ $found:       negq    $2, $3          # clear all but least set bit
        br      __stxcpy
 
        .end strcat
+EXPORT_SYMBOL(strcat);
index 011a175e8329234567c0665ec90de60ed35db88c..7412a173ea393617f1b65022b06bc8cbfe5b627e 100644 (file)
@@ -5,7 +5,7 @@
  * Return the address of a given character within a null-terminated
  * string, or null if it is not found.
  */
-
+#include <asm/export.h>
 #include <asm/regdef.h>
 
        .set noreorder
@@ -68,3 +68,4 @@ $retnull:
        ret                     # .. e1 :
 
        .end strchr
+       EXPORT_SYMBOL(strchr)
index e0728e4ad21fda604b166c9435637bd9c5af11fe..98deae1e4d085d5dfb54210aeeb854b6328af6a5 100644 (file)
@@ -5,7 +5,7 @@
  * Copy a null-terminated string from SRC to DST.  Return a pointer
  * to the null-terminator in the source.
  */
-
+#include <asm/export.h>
        .text
 
        .align 3
@@ -21,3 +21,4 @@ strcpy:
        br      __stxcpy        # do the copy
 
        .end strcpy
+       EXPORT_SYMBOL(strcpy)
index fe63353de152d3bf1d5a24b18b704bde21c161f2..79c416f71bacd5704a8d9f6a2841b9cb9566606b 100644 (file)
@@ -11,7 +11,7 @@
  *       do this instead of the 9 instructions that
  *       binary search needs).
  */
-
+#include <asm/export.h>
        .set noreorder
        .set noat
 
@@ -55,3 +55,4 @@ done: subq    $0, $16, $0
        ret     $31, ($26)
 
        .end    strlen
+       EXPORT_SYMBOL(strlen)
index a8278163c97204d3d5d2e0eb73c956c327bdfbd6..6c29ea60869ae4c3a3d3c4bff10839b1a764e67d 100644 (file)
@@ -9,7 +9,7 @@
  * past count, whereas libc may write to count+1.  This follows the generic
  * implementation in lib/string.c and is, IMHO, more sensible.
  */
-
+#include <asm/export.h>
        .text
 
        .align 3
@@ -82,3 +82,4 @@ $zerocount:
        ret
 
        .end strncat
+       EXPORT_SYMBOL(strncat)
index a46f7f3ad8c731961dd3f24502955ab5953477ca..e102cf1567ddd21314efd9d1fd1f8725cb538410 100644 (file)
@@ -10,7 +10,7 @@
  * version has cropped that bit o' nastiness as well as assuming that
  * __stxncpy is in range of a branch.
  */
-
+#include <asm/export.h>
        .set noat
        .set noreorder
 
@@ -79,3 +79,4 @@ $zerolen:
        ret
 
        .end    strncpy
+       EXPORT_SYMBOL(strncpy)
index 1970dc07cfd1248147bc17da538196fcb9cd9e84..4bc6cb4b9812e1e42a2ac2db7df182cd9810dafb 100644 (file)
@@ -5,7 +5,7 @@
  * Return the address of the last occurrence of a given character
  * within a null-terminated string, or null if it is not found.
  */
-
+#include <asm/export.h>
 #include <asm/regdef.h>
 
        .set noreorder
@@ -85,3 +85,4 @@ $retnull:
        ret                     # .. e1 :
 
        .end strrchr
+       EXPORT_SYMBOL(strrchr)
index 6cb3736b6b83613a95180cd3e9cb5ba12a9b2f7f..d347bbc086fed124627bccaebec9089652a6b7b8 100644 (file)
@@ -107,13 +107,13 @@ static int restore_usr_regs(struct pt_regs *regs, struct rt_sigframe __user *sf)
        struct user_regs_struct uregs;
 
        err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
-       if (!err)
-               set_current_blocked(&set);
-
        err |= __copy_from_user(&uregs.scratch,
                                &(sf->uc.uc_mcontext.regs.scratch),
                                sizeof(sf->uc.uc_mcontext.regs.scratch));
+       if (err)
+               return err;
 
+       set_current_blocked(&set);
        regs->bta       = uregs.scratch.bta;
        regs->lp_start  = uregs.scratch.lp_start;
        regs->lp_end    = uregs.scratch.lp_end;
@@ -138,7 +138,7 @@ static int restore_usr_regs(struct pt_regs *regs, struct rt_sigframe __user *sf)
        regs->r0        = uregs.scratch.r0;
        regs->sp        = uregs.scratch.sp;
 
-       return err;
+       return 0;
 }
 
 static inline int is_do_ss_needed(unsigned int magic)
index e52b82449a79528bc362d96fabe7b2d688abd78e..53994f9fbbcc066bff8a8803904d8aeb48095ce1 100644 (file)
        thermal-zones {
                cpu {
                        trips {
-                               trip {
+                               cpu-shutdown-trip {
                                        temperature = <101000>;
                                        hysteresis = <0>;
                                        type = "critical";
                                };
                        };
-
-                       cooling-maps {
-                               /* There are currently no cooling maps because there are no cooling devices */
-                       };
                };
 
                mem {
                        trips {
-                               trip {
+                               mem-shutdown-trip {
                                        temperature = <101000>;
                                        hysteresis = <0>;
                                        type = "critical";
                                };
                        };
-
-                       cooling-maps {
-                               /* There are currently no cooling maps because there are no cooling devices */
-                       };
                };
 
                gpu {
                        trips {
-                               trip {
+                               gpu-shutdown-trip {
                                        temperature = <101000>;
                                        hysteresis = <0>;
                                        type = "critical";
                                };
                        };
-
-                       cooling-maps {
-                               /* There are currently no cooling maps because there are no cooling devices */
-                       };
                };
        };
 };
index ea340f9de44802124829153b7065691a83b3bc12..187a36c6d0fccb773cdcb9063b80e3422dd691ab 100644 (file)
 
        soctherm: thermal-sensor@700e2000 {
                compatible = "nvidia,tegra124-soctherm";
-               reg = <0x0 0x700e2000 0x0 0x1000>;
+               reg = <0x0 0x700e2000 0x0 0x600 /* SOC_THERM reg_base */
+                       0x0 0x60006000 0x0 0x400>; /* CAR reg_base */
+               reg-names = "soctherm-reg", "car-reg";
                interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&tegra_car TEGRA124_CLK_TSENSOR>,
                        <&tegra_car TEGRA124_CLK_SOC_THERM>;
                resets = <&tegra_car 78>;
                reset-names = "soctherm";
                #thermal-sensor-cells = <1>;
+
+               throttle-cfgs {
+                       throttle_heavy: heavy {
+                               nvidia,priority = <100>;
+                               nvidia,cpu-throt-percent = <85>;
+
+                               #cooling-cells = <2>;
+                       };
+               };
        };
 
        dfll: clock@70110000 {
 
                        thermal-sensors =
                                <&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>;
+
+                       trips {
+                               cpu-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                               cpu_throttle_trip: throttle-trip {
+                                       temperature = <100000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&cpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
                };
 
                mem {
 
                        thermal-sensors =
                                <&soctherm TEGRA124_SOCTHERM_SENSOR_MEM>;
+
+                       trips {
+                               mem-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
                };
 
                gpu {
 
                        thermal-sensors =
                                <&soctherm TEGRA124_SOCTHERM_SENSOR_GPU>;
+
+                       trips {
+                               gpu-shutdown-trip {
+                                       temperature = <101000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                               gpu_throttle_trip: throttle-trip {
+                                       temperature = <99000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&gpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
                };
 
                pllx {
 
                        thermal-sensors =
                                <&soctherm TEGRA124_SOCTHERM_SENSOR_PLLX>;
+
+                       trips {
+                               pllx-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
                };
        };
 
index 4e484f406419502d334b28f0b591cf01c408d576..c58f6841f8aaf2638bfbdba4c14f542ae283b678 100644 (file)
@@ -168,8 +168,6 @@ CONFIG_DRM_PANEL_SAMSUNG_LD9040=y
 CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=y
 CONFIG_DRM_NXP_PTN3460=y
 CONFIG_DRM_PARADE_PS8622=y
-CONFIG_EXYNOS_VIDEO=y
-CONFIG_EXYNOS_MIPI_DSI=y
 CONFIG_LCD_CLASS_DEVICE=y
 CONFIG_LCD_PLATFORM=y
 CONFIG_BACKLIGHT_PWM=y
index 55e0e3ea9cb6bdeb19ff5c47385a571799dd71e6..0745538b26d3f0f439780ab2a943e4cd47f3ef33 100644 (file)
@@ -8,6 +8,7 @@ generic-y += early_ioremap.h
 generic-y += emergency-restart.h
 generic-y += errno.h
 generic-y += exec.h
+generic-y += export.h
 generic-y += ioctl.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
index a93c0f99acf7767c680158cf96acef87d1f0da51..1f59ea051bab814132074b09f55d3a57c800a471 100644 (file)
@@ -533,11 +533,12 @@ __clear_user(void __user *addr, unsigned long n)
 
 static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
 {
-       if (access_ok(VERIFY_READ, from, n))
-               n = __copy_from_user(to, from, n);
-       else /* security hole - plug it */
-               memset(to, 0, n);
-       return n;
+       unsigned long res = n;
+       if (likely(access_ok(VERIFY_READ, from, n)))
+               res = __copy_from_user(to, from, n);
+       if (unlikely(res))
+               memset(to + (n - res), 0, res);
+       return res;
 }
 
 static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
index ad325a8c7e1e5d1dfbad11e8846169696d36e6bc..68c2c097cffea17498b61c33cfe6d488dfa67475 100644 (file)
@@ -33,7 +33,7 @@ endif
 obj-$(CONFIG_CPU_IDLE)         += cpuidle.o
 obj-$(CONFIG_ISA_DMA_API)      += dma.o
 obj-$(CONFIG_FIQ)              += fiq.o fiqasm.o
-obj-$(CONFIG_MODULES)          += armksyms.o module.o
+obj-$(CONFIG_MODULES)          += module.o
 obj-$(CONFIG_ARM_MODULE_PLTS)  += module-plts.o
 obj-$(CONFIG_ISA_DMA)          += dma-isa.o
 obj-$(CONFIG_PCI)              += bios32.o isa.o
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
deleted file mode 100644 (file)
index 7e45f69..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- *  linux/arch/arm/kernel/armksyms.c
- *
- *  Copyright (C) 2000 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/export.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/cryptohash.h>
-#include <linux/delay.h>
-#include <linux/in6.h>
-#include <linux/syscalls.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-#include <linux/arm-smccc.h>
-
-#include <asm/checksum.h>
-#include <asm/ftrace.h>
-
-/*
- * libgcc functions - functions that are used internally by the
- * compiler...  (prototypes are not correct though, but that
- * doesn't really matter since they're not versioned).
- */
-extern void __ashldi3(void);
-extern void __ashrdi3(void);
-extern void __divsi3(void);
-extern void __lshrdi3(void);
-extern void __modsi3(void);
-extern void __muldi3(void);
-extern void __ucmpdi2(void);
-extern void __udivsi3(void);
-extern void __umodsi3(void);
-extern void __do_div64(void);
-extern void __bswapsi2(void);
-extern void __bswapdi2(void);
-
-extern void __aeabi_idiv(void);
-extern void __aeabi_idivmod(void);
-extern void __aeabi_lasr(void);
-extern void __aeabi_llsl(void);
-extern void __aeabi_llsr(void);
-extern void __aeabi_lmul(void);
-extern void __aeabi_uidiv(void);
-extern void __aeabi_uidivmod(void);
-extern void __aeabi_ulcmp(void);
-
-extern void fpundefinstr(void);
-
-void mmioset(void *, unsigned int, size_t);
-void mmiocpy(void *, const void *, size_t);
-
-       /* platform dependent support */
-EXPORT_SYMBOL(arm_delay_ops);
-
-       /* networking */
-EXPORT_SYMBOL(csum_partial);
-EXPORT_SYMBOL(csum_partial_copy_from_user);
-EXPORT_SYMBOL(csum_partial_copy_nocheck);
-EXPORT_SYMBOL(__csum_ipv6_magic);
-
-       /* io */
-#ifndef __raw_readsb
-EXPORT_SYMBOL(__raw_readsb);
-#endif
-#ifndef __raw_readsw
-EXPORT_SYMBOL(__raw_readsw);
-#endif
-#ifndef __raw_readsl
-EXPORT_SYMBOL(__raw_readsl);
-#endif
-#ifndef __raw_writesb
-EXPORT_SYMBOL(__raw_writesb);
-#endif
-#ifndef __raw_writesw
-EXPORT_SYMBOL(__raw_writesw);
-#endif
-#ifndef __raw_writesl
-EXPORT_SYMBOL(__raw_writesl);
-#endif
-
-       /* string / mem functions */
-EXPORT_SYMBOL(strchr);
-EXPORT_SYMBOL(strrchr);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(memchr);
-EXPORT_SYMBOL(__memzero);
-
-EXPORT_SYMBOL(mmioset);
-EXPORT_SYMBOL(mmiocpy);
-
-#ifdef CONFIG_MMU
-EXPORT_SYMBOL(copy_page);
-
-EXPORT_SYMBOL(arm_copy_from_user);
-EXPORT_SYMBOL(arm_copy_to_user);
-EXPORT_SYMBOL(arm_clear_user);
-
-EXPORT_SYMBOL(__get_user_1);
-EXPORT_SYMBOL(__get_user_2);
-EXPORT_SYMBOL(__get_user_4);
-EXPORT_SYMBOL(__get_user_8);
-
-#ifdef __ARMEB__
-EXPORT_SYMBOL(__get_user_64t_1);
-EXPORT_SYMBOL(__get_user_64t_2);
-EXPORT_SYMBOL(__get_user_64t_4);
-EXPORT_SYMBOL(__get_user_32t_8);
-#endif
-
-EXPORT_SYMBOL(__put_user_1);
-EXPORT_SYMBOL(__put_user_2);
-EXPORT_SYMBOL(__put_user_4);
-EXPORT_SYMBOL(__put_user_8);
-#endif
-
-       /* gcc lib functions */
-EXPORT_SYMBOL(__ashldi3);
-EXPORT_SYMBOL(__ashrdi3);
-EXPORT_SYMBOL(__divsi3);
-EXPORT_SYMBOL(__lshrdi3);
-EXPORT_SYMBOL(__modsi3);
-EXPORT_SYMBOL(__muldi3);
-EXPORT_SYMBOL(__ucmpdi2);
-EXPORT_SYMBOL(__udivsi3);
-EXPORT_SYMBOL(__umodsi3);
-EXPORT_SYMBOL(__do_div64);
-EXPORT_SYMBOL(__bswapsi2);
-EXPORT_SYMBOL(__bswapdi2);
-
-#ifdef CONFIG_AEABI
-EXPORT_SYMBOL(__aeabi_idiv);
-EXPORT_SYMBOL(__aeabi_idivmod);
-EXPORT_SYMBOL(__aeabi_lasr);
-EXPORT_SYMBOL(__aeabi_llsl);
-EXPORT_SYMBOL(__aeabi_llsr);
-EXPORT_SYMBOL(__aeabi_lmul);
-EXPORT_SYMBOL(__aeabi_uidiv);
-EXPORT_SYMBOL(__aeabi_uidivmod);
-EXPORT_SYMBOL(__aeabi_ulcmp);
-#endif
-
-       /* bitops */
-EXPORT_SYMBOL(_set_bit);
-EXPORT_SYMBOL(_test_and_set_bit);
-EXPORT_SYMBOL(_clear_bit);
-EXPORT_SYMBOL(_test_and_clear_bit);
-EXPORT_SYMBOL(_change_bit);
-EXPORT_SYMBOL(_test_and_change_bit);
-EXPORT_SYMBOL(_find_first_zero_bit_le);
-EXPORT_SYMBOL(_find_next_zero_bit_le);
-EXPORT_SYMBOL(_find_first_bit_le);
-EXPORT_SYMBOL(_find_next_bit_le);
-
-#ifdef __ARMEB__
-EXPORT_SYMBOL(_find_first_zero_bit_be);
-EXPORT_SYMBOL(_find_next_zero_bit_be);
-EXPORT_SYMBOL(_find_first_bit_be);
-EXPORT_SYMBOL(_find_next_bit_be);
-#endif
-
-#ifdef CONFIG_FUNCTION_TRACER
-#ifdef CONFIG_OLD_MCOUNT
-EXPORT_SYMBOL(mcount);
-#endif
-EXPORT_SYMBOL(__gnu_mcount_nc);
-#endif
-
-#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
-EXPORT_SYMBOL(__pv_phys_pfn_offset);
-EXPORT_SYMBOL(__pv_offset);
-#endif
-
-#ifdef CONFIG_HAVE_ARM_SMCCC
-EXPORT_SYMBOL(arm_smccc_smc);
-EXPORT_SYMBOL(arm_smccc_hvc);
-#endif
index c73c4030ca5dd549e3b102d4a77f493e1549e02d..b629d3f11c3da31af54c09159b581ebe30e390b2 100644 (file)
@@ -7,6 +7,7 @@
 #include <asm/assembler.h>
 #include <asm/ftrace.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 #include "entry-header.S"
 
@@ -153,6 +154,7 @@ ENTRY(mcount)
        __mcount _old
 #endif
 ENDPROC(mcount)
+EXPORT_SYMBOL(mcount)
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 ENTRY(ftrace_caller_old)
@@ -205,6 +207,7 @@ UNWIND(.fnstart)
 #endif
 UNWIND(.fnend)
 ENDPROC(__gnu_mcount_nc)
+EXPORT_SYMBOL(__gnu_mcount_nc)
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 ENTRY(ftrace_caller)
index 04286fd9e09ce7a27259c4d375a05a965e3be0ea..f41cee4c57467c5c6ab66d9f1d93d048f9b30171 100644 (file)
@@ -22,6 +22,7 @@
 #include <asm/memory.h>
 #include <asm/thread_info.h>
 #include <asm/pgtable.h>
+#include <asm/export.h>
 
 #if defined(CONFIG_DEBUG_LL) && !defined(CONFIG_DEBUG_SEMIHOSTING)
 #include CONFIG_DEBUG_LL_INCLUDE
@@ -727,6 +728,8 @@ __pv_phys_pfn_offset:
 __pv_offset:
        .quad   0
        .size   __pv_offset, . -__pv_offset
+EXPORT_SYMBOL(__pv_phys_pfn_offset)
+EXPORT_SYMBOL(__pv_offset)
 #endif
 
 #include "head-common.S"
index 2e48b674aab190563d321902be9919167d8f80cd..37669e7e13afd5483dc0acdfef22a77e9a090100 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/opcodes-sec.h>
 #include <asm/opcodes-virt.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
        /*
         * Wrap c macros in asm macros to delay expansion until after the
@@ -51,6 +52,7 @@ UNWIND(       .fnend)
 ENTRY(arm_smccc_smc)
        SMCCC SMCCC_SMC
 ENDPROC(arm_smccc_smc)
+EXPORT_SYMBOL(arm_smccc_smc)
 
 /*
  * void smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2,
@@ -60,3 +62,4 @@ ENDPROC(arm_smccc_smc)
 ENTRY(arm_smccc_hvc)
        SMCCC SMCCC_HVC
 ENDPROC(arm_smccc_hvc)
+EXPORT_SYMBOL(arm_smccc_hvc)
index b05e95840651d0f5acc653a1efaeecd5c85b18fe..a7e7de89bd75c651e43c498384e97e39b98e5512 100644 (file)
@@ -28,6 +28,7 @@ Boston, MA 02110-1301, USA.  */
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define al r1
@@ -52,3 +53,5 @@ ENTRY(__aeabi_llsl)
 
 ENDPROC(__ashldi3)
 ENDPROC(__aeabi_llsl)
+EXPORT_SYMBOL(__ashldi3)
+EXPORT_SYMBOL(__aeabi_llsl)
index 275d7d2341a4e52e31e19924ebde00aaa771a49c..490336e42518dddfc92b93cf4ef64c4105c377ec 100644 (file)
@@ -28,6 +28,7 @@ Boston, MA 02110-1301, USA.  */
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define al r1
@@ -52,3 +53,5 @@ ENTRY(__aeabi_lasr)
 
 ENDPROC(__ashrdi3)
 ENDPROC(__aeabi_lasr)
+EXPORT_SYMBOL(__ashrdi3)
+EXPORT_SYMBOL(__aeabi_lasr)
index 7d807cfd8ef57ed2bdde29d98ddb6a7094f725f7..df06638b327cda70a5421b851467bb88c6d3fa08 100644 (file)
@@ -1,5 +1,6 @@
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 #if __LINUX_ARM_ARCH__ >= 6
        .macro  bitop, name, instr
@@ -25,6 +26,7 @@ UNWIND(       .fnstart        )
        bx      lr
 UNWIND(        .fnend          )
 ENDPROC(\name          )
+EXPORT_SYMBOL(\name    )
        .endm
 
        .macro  testop, name, instr, store
@@ -55,6 +57,7 @@ UNWIND(       .fnstart        )
 2:     bx      lr
 UNWIND(        .fnend          )
 ENDPROC(\name          )
+EXPORT_SYMBOL(\name    )
        .endm
 #else
        .macro  bitop, name, instr
@@ -74,6 +77,7 @@ UNWIND(       .fnstart        )
        ret     lr
 UNWIND(        .fnend          )
 ENDPROC(\name          )
+EXPORT_SYMBOL(\name    )
        .endm
 
 /**
@@ -102,5 +106,6 @@ UNWIND(     .fnstart        )
        ret     lr
 UNWIND(        .fnend          )
 ENDPROC(\name          )
+EXPORT_SYMBOL(\name    )
        .endm
 #endif
index 07cda737bb11f14ed1c873e7e2c5b97c2da2543e..f05f78247304bb41d18dda92c6c674e86a84c90b 100644 (file)
@@ -1,5 +1,6 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #if __LINUX_ARM_ARCH__ >= 6
 ENTRY(__bswapsi2)
@@ -35,3 +36,5 @@ ENTRY(__bswapdi2)
        ret lr
 ENDPROC(__bswapdi2)
 #endif
+EXPORT_SYMBOL(__bswapsi2)
+EXPORT_SYMBOL(__bswapdi2)
index e936352ccb0013e040fcd9b22bda1c583cfff361..b566154f5cf4ede9b1e487570519d2b01309f771 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
                .text
 
@@ -50,6 +51,9 @@ USER(         strnebt r2, [r0])
 UNWIND(.fnend)
 ENDPROC(arm_clear_user)
 ENDPROC(__clear_user_std)
+#ifndef CONFIG_UACCESS_WITH_MEMCPY
+EXPORT_SYMBOL(arm_clear_user)
+#endif
 
                .pushsection .text.fixup,"ax"
                .align  0
index 1512bebfbf1b18ad317648891385a24e93d1f35f..63e4c1ed0225c8770fc9e29f311809bacdd2d95f 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 /*
  * Prototype:
@@ -94,16 +95,14 @@ ENTRY(arm_copy_from_user)
 #include "copy_template.S"
 
 ENDPROC(arm_copy_from_user)
+EXPORT_SYMBOL(arm_copy_from_user)
 
        .pushsection .fixup,"ax"
        .align 0
        copy_abort_preamble
-       ldmfd   sp!, {r1, r2}
-       sub     r3, r0, r1
-       rsb     r1, r3, r2
-       str     r1, [sp]
-       bl      __memzero
-       ldr     r0, [sp], #4
+       ldmfd   sp!, {r1, r2, r3}
+       sub     r0, r0, r1
+       rsb     r0, r0, r2
        copy_abort_end
        .popsection
 
index 6ee2f6706f869b03c95f30d2b1a1cf7adf9086d9..d97851d4af7a43f818240a687babff17ded0f06f 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/assembler.h>
 #include <asm/asm-offsets.h>
 #include <asm/cache.h>
+#include <asm/export.h>
 
 #define COPY_COUNT (PAGE_SZ / (2 * L1_CACHE_BYTES) PLD( -1 ))
 
@@ -45,3 +46,4 @@ ENTRY(copy_page)
        PLD(    beq     2b                      )
                ldmfd   sp!, {r4, pc}                   @       3
 ENDPROC(copy_page)
+EXPORT_SYMBOL(copy_page)
index caf5019d8161e2f1914a797a4c6800844a27d570..592c179112d1b5a2810c869774a2721be95e2a4d 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 /*
  * Prototype:
@@ -99,6 +100,9 @@ WEAK(arm_copy_to_user)
 
 ENDPROC(arm_copy_to_user)
 ENDPROC(__copy_to_user_std)
+#ifndef CONFIG_UACCESS_WITH_MEMCPY
+EXPORT_SYMBOL(arm_copy_to_user)
+#endif
 
        .pushsection .text.fixup,"ax"
        .align 0
index 3ac6ef01bc43a4cc13465822ead08f18eb88ea0c..68603b5ee53723d9e75f15922d0c193c2579e4e2 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .text
 
@@ -30,4 +31,4 @@ ENTRY(__csum_ipv6_magic)
                adcs    r0, r0, #0
                ldmfd   sp!, {pc}
 ENDPROC(__csum_ipv6_magic)
-
+EXPORT_SYMBOL(__csum_ipv6_magic)
index 984e0f29d548b456884e643d9f9337e4cd42fc31..830b20e81c3783a7f27c209d401b3281eb5846d1 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .text
 
@@ -140,3 +141,4 @@ ENTRY(csum_partial)
                bne     4b
                b       .Lless4
 ENDPROC(csum_partial)
+EXPORT_SYMBOL(csum_partial)
index d03fc71fc88c9d5167290ee0d2228f5efca820ec..9c3383fed129b9b9de1edb573ef4b23ac2505c1d 100644 (file)
@@ -49,5 +49,6 @@
 
 #define FN_ENTRY       ENTRY(csum_partial_copy_nocheck)
 #define FN_EXIT                ENDPROC(csum_partial_copy_nocheck)
+#define FN_EXPORT      EXPORT_SYMBOL(csum_partial_copy_nocheck)
 
 #include "csumpartialcopygeneric.S"
index 10b45909610ca6f4ca6f6f8bdc664b79c2f2bd6f..8b94d20e51d1757706773cd6bc7fad513a16fc05 100644 (file)
@@ -8,6 +8,7 @@
  * published by the Free Software Foundation.
  */
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 /*
  * unsigned int
@@ -331,3 +332,4 @@ FN_ENTRY
                mov     r5, r4, get_byte_1
                b       .Lexit
 FN_EXIT
+FN_EXPORT
index 1712f132b80d2402d94d72ea974a0c3326fa2f52..5d495edf3d83f5e166b98f2344f5553e2b1866d8 100644 (file)
@@ -73,6 +73,7 @@
 
 #define FN_ENTRY       ENTRY(csum_partial_copy_from_user)
 #define FN_EXIT                ENDPROC(csum_partial_copy_from_user)
+#define FN_EXPORT      EXPORT_SYMBOL(csum_partial_copy_from_user)
 
 #include "csumpartialcopygeneric.S"
 
index 2cef11884857dd952f1f3e99bb259470fd3e3d78..69aad80a3af4bd7bfe8f4369bd0e09ac413ff39f 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/export.h>
 #include <linux/timex.h>
 
 /*
@@ -34,6 +35,7 @@ struct arm_delay_ops arm_delay_ops __ro_after_init = {
        .const_udelay   = __loop_const_udelay,
        .udelay         = __loop_udelay,
 };
+EXPORT_SYMBOL(arm_delay_ops);
 
 static const struct delay_timer *delay_timer;
 static bool delay_calibrated;
index a9eafe4981eb847e2f07e0e245aa8e1f1747fa59..0c9e1c18fc9eca384d4d98c3eacb2993a1f0f94f 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define xh r0
@@ -210,3 +211,4 @@ Ldiv0_64:
 
 UNWIND(.fnend)
 ENDPROC(__do_div64)
+EXPORT_SYMBOL(__do_div64)
index 7848780e883473ac21d97ed54b99ec3788672980..26302b8cd38fba8c61d40e3bedc901637efc9f19 100644 (file)
@@ -15,6 +15,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
                 .text
 
 /*
@@ -37,6 +38,7 @@ ENTRY(_find_first_zero_bit_le)
 3:             mov     r0, r1                  @ no free bits
                ret     lr
 ENDPROC(_find_first_zero_bit_le)
+EXPORT_SYMBOL(_find_first_zero_bit_le)
 
 /*
  * Purpose  : Find next 'zero' bit
@@ -57,6 +59,7 @@ ENTRY(_find_next_zero_bit_le)
                add     r2, r2, #1              @ align bit pointer
                b       2b                      @ loop for next bit
 ENDPROC(_find_next_zero_bit_le)
+EXPORT_SYMBOL(_find_next_zero_bit_le)
 
 /*
  * Purpose  : Find a 'one' bit
@@ -78,6 +81,7 @@ ENTRY(_find_first_bit_le)
 3:             mov     r0, r1                  @ no free bits
                ret     lr
 ENDPROC(_find_first_bit_le)
+EXPORT_SYMBOL(_find_first_bit_le)
 
 /*
  * Purpose  : Find next 'one' bit
@@ -97,6 +101,7 @@ ENTRY(_find_next_bit_le)
                add     r2, r2, #1              @ align bit pointer
                b       2b                      @ loop for next bit
 ENDPROC(_find_next_bit_le)
+EXPORT_SYMBOL(_find_next_bit_le)
 
 #ifdef __ARMEB__
 
@@ -116,6 +121,7 @@ ENTRY(_find_first_zero_bit_be)
 3:             mov     r0, r1                  @ no free bits
                ret     lr
 ENDPROC(_find_first_zero_bit_be)
+EXPORT_SYMBOL(_find_first_zero_bit_be)
 
 ENTRY(_find_next_zero_bit_be)
                teq     r1, #0
@@ -133,6 +139,7 @@ ENTRY(_find_next_zero_bit_be)
                add     r2, r2, #1              @ align bit pointer
                b       2b                      @ loop for next bit
 ENDPROC(_find_next_zero_bit_be)
+EXPORT_SYMBOL(_find_next_zero_bit_be)
 
 ENTRY(_find_first_bit_be)
                teq     r1, #0
@@ -150,6 +157,7 @@ ENTRY(_find_first_bit_be)
 3:             mov     r0, r1                  @ no free bits
                ret     lr
 ENDPROC(_find_first_bit_be)
+EXPORT_SYMBOL(_find_first_bit_be)
 
 ENTRY(_find_next_bit_be)
                teq     r1, #0
@@ -166,6 +174,7 @@ ENTRY(_find_next_bit_be)
                add     r2, r2, #1              @ align bit pointer
                b       2b                      @ loop for next bit
 ENDPROC(_find_next_bit_be)
+EXPORT_SYMBOL(_find_next_bit_be)
 
 #endif
 
index 8ecfd15c3a0248db29667fe3dc6ec6429fc9fc7c..9d09a38e73af361e6939e53cb0303e14322a750e 100644 (file)
@@ -31,6 +31,7 @@
 #include <asm/assembler.h>
 #include <asm/errno.h>
 #include <asm/domain.h>
+#include <asm/export.h>
 
 ENTRY(__get_user_1)
        check_uaccess r0, 1, r1, r2, __get_user_bad
@@ -38,6 +39,7 @@ ENTRY(__get_user_1)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_1)
+EXPORT_SYMBOL(__get_user_1)
 
 ENTRY(__get_user_2)
        check_uaccess r0, 2, r1, r2, __get_user_bad
@@ -58,6 +60,7 @@ rb    .req    r0
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_2)
+EXPORT_SYMBOL(__get_user_2)
 
 ENTRY(__get_user_4)
        check_uaccess r0, 4, r1, r2, __get_user_bad
@@ -65,6 +68,7 @@ ENTRY(__get_user_4)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_4)
+EXPORT_SYMBOL(__get_user_4)
 
 ENTRY(__get_user_8)
        check_uaccess r0, 8, r1, r2, __get_user_bad
@@ -78,6 +82,7 @@ ENTRY(__get_user_8)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_8)
+EXPORT_SYMBOL(__get_user_8)
 
 #ifdef __ARMEB__
 ENTRY(__get_user_32t_8)
@@ -91,6 +96,7 @@ ENTRY(__get_user_32t_8)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_32t_8)
+EXPORT_SYMBOL(__get_user_32t_8)
 
 ENTRY(__get_user_64t_1)
        check_uaccess r0, 1, r1, r2, __get_user_bad8
@@ -98,6 +104,7 @@ ENTRY(__get_user_64t_1)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_64t_1)
+EXPORT_SYMBOL(__get_user_64t_1)
 
 ENTRY(__get_user_64t_2)
        check_uaccess r0, 2, r1, r2, __get_user_bad8
@@ -114,6 +121,7 @@ rb  .req    r0
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_64t_2)
+EXPORT_SYMBOL(__get_user_64t_2)
 
 ENTRY(__get_user_64t_4)
        check_uaccess r0, 4, r1, r2, __get_user_bad8
@@ -121,6 +129,7 @@ ENTRY(__get_user_64t_4)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_64t_4)
+EXPORT_SYMBOL(__get_user_64t_4)
 #endif
 
 __get_user_bad8:
index c31b2f3153f171fd09602aed2ea9cb8c97797f4d..3dff7a3a2aef057ffb55525ded4cf10d45137f0c 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 .Linsb_align:  rsb     ip, ip, #4
                cmp     ip, r2
@@ -121,3 +122,4 @@ ENTRY(__raw_readsb)
 
                ldmfd   sp!, {r4 - r6, pc}
 ENDPROC(__raw_readsb)
+EXPORT_SYMBOL(__raw_readsb)
index 2ed86fa5465f70cdcb92a46a167d9aa81edad68f..bfd39682325b0c43ba990a130cc14afca9d87104 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 ENTRY(__raw_readsl)
                teq     r2, #0          @ do we have to check for the zero len?
@@ -77,3 +78,4 @@ ENTRY(__raw_readsl)
                strb    r3, [r1, #0]
                ret     lr
 ENDPROC(__raw_readsl)
+EXPORT_SYMBOL(__raw_readsl)
index 413da99145292f3e535b618fee2a5c9c96e114b4..b3af3db6caac8bd7e2a41f21c0098dbb00b5568f 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 .Linsw_bad_alignment:
                adr     r0, .Linsw_bad_align_msg
@@ -103,4 +104,4 @@ ENTRY(__raw_readsw)
 
                ldmfd   sp!, {r4, r5, r6, pc}
 
-
+EXPORT_SYMBOL(__raw_readsw)
index d9a45e9692aee3ad1de5dea37653a65cd8c18da4..3c7a7a40b33ead61b3e1255674243e7894c92963 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .macro  pack, rd, hw1, hw2
 #ifndef __ARMEB__
@@ -129,3 +130,4 @@ ENTRY(__raw_readsw)
                strneb  ip, [r1]
                ldmfd   sp!, {r4, pc}
 ENDPROC(__raw_readsw)
+EXPORT_SYMBOL(__raw_readsw)
index a46bbc9b168b45f7016096244eb4933a911d4ac0..fa36335944159050eceedba7fcb1505c42e79f15 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .macro  outword, rd
 #ifndef __ARMEB__
@@ -92,3 +93,4 @@ ENTRY(__raw_writesb)
 
                ldmfd   sp!, {r4, r5, pc}
 ENDPROC(__raw_writesb)
+EXPORT_SYMBOL(__raw_writesb)
index 4ea2435988c1f75d8fddac8ac63a499067d02cae..98ed6aec0b4767df744bb9ca8581a29bb507c1a3 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 ENTRY(__raw_writesl)
                teq     r2, #0          @ do we have to check for the zero len?
@@ -65,3 +66,4 @@ ENTRY(__raw_writesl)
                bne     6b
                ret     lr
 ENDPROC(__raw_writesl)
+EXPORT_SYMBOL(__raw_writesl)
index 121789eb680235f9dad2c8f1492960d2f26fded1..577184c082bb6192f5734baef70d0667dcced5c2 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 .Loutsw_bad_alignment:
                adr     r0, .Loutsw_bad_align_msg
@@ -124,3 +125,4 @@ ENTRY(__raw_writesw)
                strne   ip, [r0]
 
                ldmfd   sp!, {r4, r5, r6, pc}
+EXPORT_SYMBOL(__raw_writesw)
index 269f90c51ad279c63bf4dd9f8bfed8c6827a75d2..e335f489d1fcda8201728dd061c4802d4cbf7a58 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .macro  outword, rd
 #ifndef __ARMEB__
@@ -98,3 +99,4 @@ ENTRY(__raw_writesw)
                strneh  ip, [r0]
                ret     lr
 ENDPROC(__raw_writesw)
+EXPORT_SYMBOL(__raw_writesw)
index 9397b2e532afa3d863930b4e29a663c166ae475e..f541bc013bff63398f7462da233de6bf2cd96ee6 100644 (file)
@@ -36,6 +36,7 @@ Boston, MA 02111-1307, USA.  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 .macro ARM_DIV_BODY dividend, divisor, result, curbit
 
@@ -238,6 +239,8 @@ UNWIND(.fnstart)
 UNWIND(.fnend)
 ENDPROC(__udivsi3)
 ENDPROC(__aeabi_uidiv)
+EXPORT_SYMBOL(__udivsi3)
+EXPORT_SYMBOL(__aeabi_uidiv)
 
 ENTRY(__umodsi3)
 UNWIND(.fnstart)
@@ -256,6 +259,7 @@ UNWIND(.fnstart)
 
 UNWIND(.fnend)
 ENDPROC(__umodsi3)
+EXPORT_SYMBOL(__umodsi3)
 
 #ifdef CONFIG_ARM_PATCH_IDIV
        .align 3
@@ -303,6 +307,8 @@ UNWIND(.fnstart)
 UNWIND(.fnend)
 ENDPROC(__divsi3)
 ENDPROC(__aeabi_idiv)
+EXPORT_SYMBOL(__divsi3)
+EXPORT_SYMBOL(__aeabi_idiv)
 
 ENTRY(__modsi3)
 UNWIND(.fnstart)
@@ -327,6 +333,7 @@ UNWIND(.fnstart)
 
 UNWIND(.fnend)
 ENDPROC(__modsi3)
+EXPORT_SYMBOL(__modsi3)
 
 #ifdef CONFIG_AEABI
 
@@ -343,6 +350,7 @@ UNWIND(.save {r0, r1, ip, lr}       )
 
 UNWIND(.fnend)
 ENDPROC(__aeabi_uidivmod)
+EXPORT_SYMBOL(__aeabi_uidivmod)
 
 ENTRY(__aeabi_idivmod)
 UNWIND(.fnstart)
@@ -356,6 +364,7 @@ UNWIND(.save {r0, r1, ip, lr}       )
 
 UNWIND(.fnend)
 ENDPROC(__aeabi_idivmod)
+EXPORT_SYMBOL(__aeabi_idivmod)
 
 #endif
 
index 922dcd88b02b7804fca63f0d891e9a7ed6cbf83e..e408339814174a0b6d826699789c7f1f96778814 100644 (file)
@@ -28,6 +28,7 @@ Boston, MA 02110-1301, USA.  */
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define al r1
@@ -52,3 +53,5 @@ ENTRY(__aeabi_llsr)
 
 ENDPROC(__lshrdi3)
 ENDPROC(__aeabi_llsr)
+EXPORT_SYMBOL(__lshrdi3)
+EXPORT_SYMBOL(__aeabi_llsr)
index 74a5bed6d9999a645d06d6d34369f9b7c155c0f9..44182bf686a51cb17e483035482208a8889a28a3 100644 (file)
@@ -11,6 +11,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
        .text
        .align  5
@@ -24,3 +25,4 @@ ENTRY(memchr)
 2:     movne   r0, #0
        ret     lr
 ENDPROC(memchr)
+EXPORT_SYMBOL(memchr)
index 64111bd4440b1aa3702c469ce349b303a0244ebd..1be5b6ddf37c820910ad260a2a243515d07549c4 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 #define LDR1W_SHIFT    0
 #define STR1W_SHIFT    0
@@ -68,3 +69,5 @@ ENTRY(memcpy)
 
 ENDPROC(memcpy)
 ENDPROC(mmiocpy)
+EXPORT_SYMBOL(memcpy)
+EXPORT_SYMBOL(mmiocpy)
index 69a9d47fc5abdcb9f1801cbfe249eaed99b00d99..71dcc5400d025670ca7bdb872f24c83f88650e81 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
                .text
 
@@ -225,3 +226,4 @@ ENTRY(memmove)
 18:            backward_copy_shift     push=24 pull=8
 
 ENDPROC(memmove)
+EXPORT_SYMBOL(memmove)
index 3c65e3bd790fe1f7aec59d9568cb4c0799be0e9a..7b72044cba62f9c45d693999ebf3e6bb5bdff18a 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
        .text
        .align  5
@@ -135,3 +136,5 @@ UNWIND( .fnstart            )
 UNWIND( .fnend   )
 ENDPROC(memset)
 ENDPROC(mmioset)
+EXPORT_SYMBOL(memset)
+EXPORT_SYMBOL(mmioset)
index 0eded952e0896eddcfdb95acc3fd42cefee4aef3..6dec26ed5bccde9c6e5ba3819022e9e0cf95ea56 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
        .text
        .align  5
@@ -135,3 +136,4 @@ UNWIND(     .fnstart                        )
        ret     lr                      @ 1
 UNWIND(        .fnend                          )
 ENDPROC(__memzero)
+EXPORT_SYMBOL(__memzero)
index 20430595692500b7442a39f6b483b2af9afd0b2c..b8f12388ccaca25e4babea56e7645dfde0de425d 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define xh r0
@@ -46,3 +47,5 @@ ENTRY(__aeabi_lmul)
 
 ENDPROC(__muldi3)
 ENDPROC(__aeabi_lmul)
+EXPORT_SYMBOL(__muldi3)
+EXPORT_SYMBOL(__aeabi_lmul)
index 38d660d3705f4f259c5299d2cc8c1126f0a1dbb4..11de126e2ed6bc757f5185ccf8ef0fe5551c6c08 100644 (file)
@@ -31,6 +31,7 @@
 #include <asm/assembler.h>
 #include <asm/errno.h>
 #include <asm/domain.h>
+#include <asm/export.h>
 
 ENTRY(__put_user_1)
        check_uaccess r0, 1, r1, ip, __put_user_bad
@@ -38,6 +39,7 @@ ENTRY(__put_user_1)
        mov     r0, #0
        ret     lr
 ENDPROC(__put_user_1)
+EXPORT_SYMBOL(__put_user_1)
 
 ENTRY(__put_user_2)
        check_uaccess r0, 2, r1, ip, __put_user_bad
@@ -62,6 +64,7 @@ ENTRY(__put_user_2)
        mov     r0, #0
        ret     lr
 ENDPROC(__put_user_2)
+EXPORT_SYMBOL(__put_user_2)
 
 ENTRY(__put_user_4)
        check_uaccess r0, 4, r1, ip, __put_user_bad
@@ -69,6 +72,7 @@ ENTRY(__put_user_4)
        mov     r0, #0
        ret     lr
 ENDPROC(__put_user_4)
+EXPORT_SYMBOL(__put_user_4)
 
 ENTRY(__put_user_8)
        check_uaccess r0, 8, r1, ip, __put_user_bad
@@ -82,6 +86,7 @@ ENTRY(__put_user_8)
        mov     r0, #0
        ret     lr
 ENDPROC(__put_user_8)
+EXPORT_SYMBOL(__put_user_8)
 
 __put_user_bad:
        mov     r0, #-EFAULT
index 013d64c71e8d6aae7b0d9f826b13dbca1e32f22d..7301f6e6046c1bb33ba6e6f206418dddbdfc312b 100644 (file)
@@ -11,6 +11,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .text
                .align  5
@@ -25,3 +26,4 @@ ENTRY(strchr)
                subeq   r0, r0, #1
                ret     lr
 ENDPROC(strchr)
+EXPORT_SYMBOL(strchr)
index 3cec1c7482c49dbae6450af7bc66f72dfb7179c6..aaf9fd98b7548d0c045cfefa5e35cf5bfefb8349 100644 (file)
@@ -11,6 +11,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .text
                .align  5
@@ -24,3 +25,4 @@ ENTRY(strrchr)
                mov     r0, r3
                ret     lr
 ENDPROC(strrchr)
+EXPORT_SYMBOL(strrchr)
index 6bd1089b07e0960830ed6bd6a8345202b7efd8b0..1626e3a551a14e53e08659311122c7fc51ae557f 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/gfp.h>
 #include <linux/highmem.h>
 #include <linux/hugetlb.h>
+#include <linux/export.h>
 #include <asm/current.h>
 #include <asm/page.h>
 
@@ -156,6 +157,7 @@ arm_copy_to_user(void __user *to, const void *from, unsigned long n)
        }
        return n;
 }
+EXPORT_SYMBOL(arm_copy_to_user);
        
 static unsigned long noinline
 __clear_user_memset(void __user *addr, unsigned long n)
@@ -213,6 +215,7 @@ unsigned long arm_clear_user(void __user *addr, unsigned long n)
        }
        return n;
 }
+EXPORT_SYMBOL(arm_clear_user);
 
 #if 0
 
index ad4a6309141a59c0ba3534795b93c5dbe7876814..127a91af46f3adc2c502d674518dac7645b2bf7a 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define xh r0
@@ -35,6 +36,7 @@ ENTRY(__ucmpdi2)
        ret     lr
 
 ENDPROC(__ucmpdi2)
+EXPORT_SYMBOL(__ucmpdi2)
 
 #ifdef CONFIG_AEABI
 
@@ -48,6 +50,7 @@ ENTRY(__aeabi_ulcmp)
        ret     lr
 
 ENDPROC(__aeabi_ulcmp)
+EXPORT_SYMBOL(__aeabi_ulcmp)
 
 #endif
 
index cab128913e72a710a0f405336b0d814e56935546..737450fe790c37fdb42ec122aa5147ccbadd3cc0 100644 (file)
@@ -32,7 +32,6 @@ endif
 
 ifdef CONFIG_SND_IMX_SOC
 obj-y += ssi-fiq.o
-obj-y += ssi-fiq-ksym.o
 endif
 
 # i.MX21 based machines
diff --git a/arch/arm/mach-imx/ssi-fiq-ksym.c b/arch/arm/mach-imx/ssi-fiq-ksym.c
deleted file mode 100644 (file)
index 792090f..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Exported ksyms for the SSI FIQ handler
- *
- * Copyright (C) 2009, Sascha Hauer <s.hauer@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-
-#include <linux/platform_data/asoc-imx-ssi.h>
-
-EXPORT_SYMBOL(imx_ssi_fiq_tx_buffer);
-EXPORT_SYMBOL(imx_ssi_fiq_rx_buffer);
-EXPORT_SYMBOL(imx_ssi_fiq_start);
-EXPORT_SYMBOL(imx_ssi_fiq_end);
-EXPORT_SYMBOL(imx_ssi_fiq_base);
-
index a8b93c5f29b5321a3b0ef341cfb07d601218a2d2..fd7917f1c20410d5df584c22bc043eddb0984987 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 /*
  * r8  = bit 0-15: tx offset, bit 16-31: tx buffer size
@@ -144,4 +145,8 @@ imx_ssi_fiq_tx_buffer:
                .word 0x0
 .L_imx_ssi_fiq_end:
 imx_ssi_fiq_end:
-
+EXPORT_SYMBOL(imx_ssi_fiq_tx_buffer)
+EXPORT_SYMBOL(imx_ssi_fiq_rx_buffer)
+EXPORT_SYMBOL(imx_ssi_fiq_start)
+EXPORT_SYMBOL(imx_ssi_fiq_end)
+EXPORT_SYMBOL(imx_ssi_fiq_base)
index 58635f7f4668abbc8ebfd52ba96abafe63d817db..220ac7057d1284fa97d5ccf1787e09a2aafd60a4 100644 (file)
                };
 
                sata: sata@3200000 {
-                       compatible = "fsl,ls1043a-ahci", "fsl,ls1021a-ahci";
+                       compatible = "fsl,ls1043a-ahci";
                        reg = <0x0 0x3200000 0x0 0x10000>;
                        interrupts = <0 69 0x4>;
                        clocks = <&clockgen 4 0>;
+                       dma-coherent;
                };
 
                msi1: msi-controller1@1571000 {
index d1059765dfee4ac41831ff5faf1f2d71df9fc241..337da90bd7dade6df0d74d49a9794b6b20cc8510 100644 (file)
                        reg = <0x0 0x3200000 0x0 0x10000>;
                        interrupts = <0 133 0x4>; /* Level high type */
                        clocks = <&clockgen 4 3>;
+                       dma-coherent;
                };
 
                sata1: sata@3210000 {
                        reg = <0x0 0x3210000 0x0 0x10000>;
                        interrupts = <0 136 0x4>; /* Level high type */
                        clocks = <&clockgen 4 3>;
+                       dma-coherent;
                };
 
                usb0: usb3@3100000 {
index 2013f89160840723f79269c432fcd906b932b949..3f3a46a4bd01b0d0003ffccd14c3b106ef1138aa 100644 (file)
@@ -4,6 +4,7 @@
 #include <dt-bindings/pinctrl/pinctrl-tegra.h>
 #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/thermal/tegra124-soctherm.h>
 
 / {
        compatible = "nvidia,tegra132", "nvidia,tegra124";
        };
 
        soctherm: thermal-sensor@700e2000 {
-               compatible = "nvidia,tegra124-soctherm";
-               reg = <0x0 0x700e2000 0x0 0x1000>;
+               compatible = "nvidia,tegra132-soctherm";
+               reg = <0x0 0x700e2000 0x0 0x600 /* 0: SOC_THERM reg_base */
+                       0x0 0x70040000 0x0 0x200>; /* 2: CCROC reg_base */
+               reg-names = "soctherm-reg", "ccroc-reg";
                interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&tegra_car TEGRA124_CLK_TSENSOR>,
                        <&tegra_car TEGRA124_CLK_SOC_THERM>;
                resets = <&tegra_car 78>;
                reset-names = "soctherm";
                #thermal-sensor-cells = <1>;
+
+               throttle-cfgs {
+                       throttle_heavy: heavy {
+                               nvidia,priority = <100>;
+                               nvidia,cpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_HIGH>;
+
+                               #cooling-cells = <2>;
+                       };
+               };
+       };
+
+       thermal-zones {
+               cpu {
+                       polling-delay-passive = <1000>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>;
+
+                       trips {
+                               cpu_shutdown_trip {
+                                       temperature = <105000>;
+                                       hysteresis = <1000>;
+                                       type = "critical";
+                               };
+
+                               cpu_throttle_trip: throttle-trip {
+                                       temperature = <102000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&cpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
+               };
+               mem {
+                       polling-delay-passive = <0>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_MEM>;
+
+                       trips {
+                               mem_shutdown_trip {
+                                       temperature = <101000>;
+                                       hysteresis = <1000>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
+               };
+               gpu {
+                       polling-delay-passive = <1000>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_GPU>;
+
+                       trips {
+                               gpu_shutdown_trip {
+                                       temperature = <101000>;
+                                       hysteresis = <1000>;
+                                       type = "critical";
+                               };
+
+                               gpu_throttle_trip: throttle-trip {
+                                       temperature = <99000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&gpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
+               };
+               pllx {
+                       polling-delay-passive = <0>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_PLLX>;
+
+                       trips {
+                               pllx_shutdown_trip {
+                                       temperature = <105000>;
+                                       hysteresis = <1000>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
+               };
        };
 
        ahub@70300000 {
index f6739797150a113ef1ee37654b05c154eb3e0f06..46045fe719daf378c4ebb46b753e47243284e3e4 100644 (file)
@@ -3,6 +3,7 @@
 #include <dt-bindings/memory/tegra210-mc.h>
 #include <dt-bindings/pinctrl/pinctrl-tegra.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/thermal/tegra124-soctherm.h>
 
 / {
        compatible = "nvidia,tegra210";
                                (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
                interrupt-parent = <&gic>;
        };
+
+       soctherm: thermal-sensor@700e2000 {
+               compatible = "nvidia,tegra210-soctherm";
+               reg = <0x0 0x700e2000 0x0 0x600 /* SOC_THERM reg_base */
+                       0x0 0x60006000 0x0 0x400>; /* CAR reg_base */
+               reg-names = "soctherm-reg", "car-reg";
+               interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&tegra_car TEGRA210_CLK_TSENSOR>,
+                       <&tegra_car TEGRA210_CLK_SOC_THERM>;
+               clock-names = "tsensor", "soctherm";
+               resets = <&tegra_car 78>;
+               reset-names = "soctherm";
+               #thermal-sensor-cells = <1>;
+
+               throttle-cfgs {
+                       throttle_heavy: heavy {
+                               nvidia,priority = <100>;
+                               nvidia,cpu-throt-percent = <85>;
+
+                               #cooling-cells = <2>;
+                       };
+               };
+       };
+
+       thermal-zones {
+               cpu {
+                       polling-delay-passive = <1000>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>;
+
+                       trips {
+                               cpu-shutdown-trip {
+                                       temperature = <102500>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+
+                               cpu_throttle_trip: throttle-trip {
+                                       temperature = <98500>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&cpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
+               };
+               mem {
+                       polling-delay-passive = <0>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_MEM>;
+
+                       trips {
+                               mem-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
+               };
+               gpu {
+                       polling-delay-passive = <1000>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_GPU>;
+
+                       trips {
+                               gpu-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+
+                               gpu_throttle_trip: throttle-trip {
+                                       temperature = <100000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&gpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
+               };
+               pllx {
+                       polling-delay-passive = <0>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_PLLX>;
+
+                       trips {
+                               pllx-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
+               };
+       };
 };
index c47257c91b77e3d6516000c0c8bec5705b97b6dc..bcaf6fba1b65bd559359c35e43a43339051c5cfe 100644 (file)
@@ -278,14 +278,16 @@ static inline unsigned long __must_check __copy_to_user(void __user *to, const v
 
 static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
 {
+       unsigned long res = n;
        kasan_check_write(to, n);
 
        if (access_ok(VERIFY_READ, from, n)) {
                check_object_size(to, n, false);
-               n = __arch_copy_from_user(to, from, n);
-       } else /* security hole - plug it */
-               memset(to, 0, n);
-       return n;
+               res = __arch_copy_from_user(to, from, n);
+       }
+       if (unlikely(res))
+               memset(to + (n - res), 0, res);
+       return res;
 }
 
 static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
index b8799e7c79de51dac71c5f7485709177b7cd3b5d..1bec41b5fda3917b2ed7583663a239f25c6126bf 100644 (file)
@@ -135,7 +135,7 @@ ENTRY(_cpu_resume)
 
 #ifdef CONFIG_KASAN
        mov     x0, sp
-       bl      kasan_unpoison_remaining_stack
+       bl      kasan_unpoison_task_stack_below
 #endif
 
        ldp     x19, x20, [x29, #16]
index 0b90497d4424c59d0a9ce2dcf5642012f452d3c8..4fd67ea03bb054d44e579426def0051429579920 100644 (file)
@@ -79,11 +79,6 @@ ENDPROC(__arch_copy_from_user)
 
        .section .fixup,"ax"
        .align  2
-9998:
-       sub     x0, end, dst
-9999:
-       strb    wzr, [dst], #1                  // zero remaining buffer space
-       cmp     dst, end
-       b.lo    9999b
+9998:  sub     x0, end, dst                    // bytes not copied
        ret
        .previous
index 0a2a70096d8b2dc7345b6e1ca85c68b3cdd39b9e..0eff88aa6d6ae6626ad411876aeca9ee41887ca4 100644 (file)
@@ -163,18 +163,29 @@ static inline int bad_user_access_length(void)
                : "a" (__ptr(ptr)));            \
 })
 
-#define __copy_from_user(to, from, n) copy_from_user(to, from, n)
-#define __copy_to_user(to, from, n) copy_to_user(to, from, n)
 #define __copy_to_user_inatomic __copy_to_user
 #define __copy_from_user_inatomic __copy_from_user
 
+static inline unsigned long __must_check
+__copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+       memcpy(to, (const void __force *)from, n);
+       return 0;
+}
+
+static inline unsigned long __must_check
+__copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+       memcpy((void __force *)to, from, n);
+       SSYNC();
+       return 0;
+}
+
 static inline unsigned long __must_check
 copy_from_user(void *to, const void __user *from, unsigned long n)
 {
-       if (likely(access_ok(VERIFY_READ, from, n))) {
-               memcpy(to, (const void __force *)from, n);
-               return 0;
-       }
+       if (likely(access_ok(VERIFY_READ, from, n)))
+               return __copy_from_user(to, from, n);
        memset(to, 0, n);
        return n;
 }
@@ -182,12 +193,9 @@ copy_from_user(void *to, const void __user *from, unsigned long n)
 static inline unsigned long __must_check
 copy_to_user(void __user *to, const void *from, unsigned long n)
 {
-       if (access_ok(VERIFY_WRITE, to, n))
-               memcpy((void __force *)to, from, n);
-       else
-               return n;
-       SSYNC();
-       return 0;
+       if (likely(access_ok(VERIFY_WRITE, to, n)))
+               return __copy_to_user(to, from, n);
+       return n;
 }
 
 /*
index 8b8fe671b1a6dbf347dd4d31a7f272ef87154bcb..8d79286ee4e878044b02c963f5fb3c6deaaafb19 100644 (file)
@@ -271,7 +271,7 @@ long arch_ptrace(struct task_struct *child, long request,
                        case BFIN_MEM_ACCESS_CORE:
                        case BFIN_MEM_ACCESS_CORE_ONLY:
                                copied = access_process_vm(child, addr, &tmp,
-                                                          to_copy, 0);
+                                                          to_copy, FOLL_FORCE);
                                if (copied)
                                        break;
 
@@ -324,7 +324,8 @@ long arch_ptrace(struct task_struct *child, long request,
                        case BFIN_MEM_ACCESS_CORE:
                        case BFIN_MEM_ACCESS_CORE_ONLY:
                                copied = access_process_vm(child, addr, &data,
-                                                          to_copy, 1);
+                                                          to_copy,
+                                                          FOLL_FORCE | FOLL_WRITE);
                                break;
                        case BFIN_MEM_ACCESS_DMA:
                                if (safe_dma_memcpy(paddr, &data, to_copy))
index b5698c876fccd91c3960439753d24510ce008984..099e170a93ee76de91384666a80ea74b48629b8a 100644 (file)
@@ -2722,7 +2722,6 @@ static int cryptocop_ioctl_process(struct inode *inode, struct file *filp, unsig
        err = get_user_pages((unsigned long int)(oper.indata + prev_ix),
                             noinpages,
                             0,  /* read access only for in data */
-                            0, /* no force */
                             inpages,
                             NULL);
 
@@ -2736,8 +2735,7 @@ static int cryptocop_ioctl_process(struct inode *inode, struct file *filp, unsig
        if (oper.do_cipher){
                err = get_user_pages((unsigned long int)oper.cipher_outdata,
                                     nooutpages,
-                                    1, /* write access for out data */
-                                    0, /* no force */
+                                    FOLL_WRITE, /* write access for out data */
                                     outpages,
                                     NULL);
                up_read(&current->mm->mmap_sem);
index f085229cf870bc306b95df2dee4eadd9ebdd4b8b..f0df654ac6fc5ca53ffd6d6951817e92f492e0be 100644 (file)
@@ -147,7 +147,7 @@ long arch_ptrace(struct task_struct *child, long request,
                                /* The trampoline page is globally mapped, no page table to traverse.*/
                                tmp = *(unsigned long*)addr;
                        } else {
-                               copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+                               copied = access_process_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE);
 
                                if (copied != sizeof(tmp))
                                        break;
@@ -279,7 +279,7 @@ static int insn_size(struct task_struct *child, unsigned long pc)
   int opsize = 0;
 
   /* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */
-  copied = access_process_vm(child, pc, &opcode, sizeof(opcode), 0);
+  copied = access_process_vm(child, pc, &opcode, sizeof(opcode), FOLL_FORCE);
   if (copied != sizeof(opcode))
     return 0;
 
index 2e805e0cc56059f51074ec738010a81d2232605f..df6e9968c84541c0dafdd3306b452fa762e3185f 100644 (file)
@@ -33,5 +33,5 @@ $(obj)/vmlinux.bin: vmlinux FORCE
 LDFLAGS_bootloader = -static -T
 
 $(obj)/bootloader: $(src)/bootloader.lds $(obj)/bootloader.o $(obj)/boot_head.o $(obj)/fw-emu.o \
-                   lib/lib.a arch/ia64/lib/built-in.o arch/ia64/lib/lib.a FORCE
+                   lib/lib.a arch/ia64/lib/lib.a FORCE
        $(call if_changed,ld)
diff --git a/arch/ia64/include/asm/export.h b/arch/ia64/include/asm/export.h
new file mode 100644 (file)
index 0000000..ad18c65
--- /dev/null
@@ -0,0 +1,3 @@
+/* EXPORT_DATA_SYMBOL != EXPORT_SYMBOL here */
+#define KSYM_FUNC(name) @fptr(name)
+#include <asm-generic/export.h>
index 0e00c9a9f4100b48e0302c13c43c90962c38d631..7a1f8310596bede26f78d60a2bee0a4ac97a9242 100644 (file)
@@ -1,12 +1,8 @@
 #ifndef __ASM_IA64_LIBATA_PORTMAP_H
 #define __ASM_IA64_LIBATA_PORTMAP_H
 
-#define ATA_PRIMARY_CMD                0x1F0
-#define ATA_PRIMARY_CTL                0x3F6
 #define ATA_PRIMARY_IRQ(dev)   isa_irq_to_vector(14)
 
-#define ATA_SECONDARY_CMD      0x170
-#define ATA_SECONDARY_CTL      0x376
 #define ATA_SECONDARY_IRQ(dev) isa_irq_to_vector(15)
 
 #endif
index cfaa7b25084c5384e849af309b0245a652b34e67..6f27a663177c4338299d98d33145a0c60546bf7a 100644 (file)
@@ -48,6 +48,7 @@
 #include <asm/thread_info.h>
 #include <asm/unistd.h>
 #include <asm/ftrace.h>
+#include <asm/export.h>
 
 #include "minstate.h"
 
@@ -1345,12 +1346,14 @@ GLOBAL_ENTRY(unw_init_running)
        mov rp=loc0
        br.ret.sptk.many rp
 END(unw_init_running)
+EXPORT_SYMBOL(unw_init_running)
 
 #ifdef CONFIG_FUNCTION_TRACER
 #ifdef CONFIG_DYNAMIC_FTRACE
 GLOBAL_ENTRY(_mcount)
        br ftrace_stub
 END(_mcount)
+EXPORT_SYMBOL(_mcount)
 
 .here:
        br.ret.sptk.many b0
index 09f845793d12c1147bc767885673545a3bef88f3..5ed0ea92c5bfac3935b3b799607dca9b3b6086a9 100644 (file)
@@ -142,7 +142,7 @@ store_virtual_to_phys(struct device *dev, struct device_attribute *attr,
        u64 virt_addr=simple_strtoull(buf, NULL, 16);
        int ret;
 
-       ret = get_user_pages(virt_addr, 1, VM_READ, 0, NULL, NULL);
+       ret = get_user_pages(virt_addr, 1, FOLL_WRITE, NULL, NULL);
        if (ret<=0) {
 #ifdef ERR_INJ_DEBUG
                printk("Virtual address %lx is not existing.\n",virt_addr);
index 6b3d6c1f99b6db32c2208401ab4ae81c51acfe59..2c369bf77c4bc92df695f1d7cb34584355534499 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <asm/processor.h>
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 /*
  * Inputs:
@@ -94,3 +95,4 @@ GLOBAL_ENTRY(esi_call_phys)
        mov gp=loc2
        br.ret.sptk.many rp
 END(esi_call_phys)
+EXPORT_SYMBOL_GPL(esi_call_phys)
index bb748c5964433165efab01ba39a33669fe76069a..c9b5e942f67156f5b6b7cf4917658fbf85f1544f 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/mca_asm.h>
 #include <linux/init.h>
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 #ifdef CONFIG_HOTPLUG_CPU
 #define SAL_PSR_BITS_TO_SET                            \
@@ -168,6 +169,7 @@ RestRR:                                                                                     \
        __PAGE_ALIGNED_DATA
 
        .global empty_zero_page
+EXPORT_DATA_SYMBOL_GPL(empty_zero_page)
 empty_zero_page:
        .skip PAGE_SIZE
 
index 09673104953828129add010722331770c426bf42..d111248af7191ad5480b554e370effb015e54245 100644 (file)
 /*
  * Architecture-specific kernel symbols
- *
- * Don't put any exports here unless it's defined in an assembler file.
- * All other exports should be put directly after the definition.
  */
 
-#include <linux/module.h>
-
-#include <linux/string.h>
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(strlen);
-
-#include <asm/pgtable.h>
-EXPORT_SYMBOL_GPL(empty_zero_page);
-
-#include <asm/checksum.h>
-EXPORT_SYMBOL(ip_fast_csum);           /* hand-coded assembly */
-EXPORT_SYMBOL(csum_ipv6_magic);
-
-#include <asm/page.h>
-EXPORT_SYMBOL(clear_page);
-EXPORT_SYMBOL(copy_page);
-
 #ifdef CONFIG_VIRTUAL_MEM_MAP
+#include <linux/compiler.h>
+#include <linux/export.h>
 #include <linux/bootmem.h>
 EXPORT_SYMBOL(min_low_pfn);    /* defined by bootmem.c, but not exported by generic code */
 EXPORT_SYMBOL(max_low_pfn);    /* defined by bootmem.c, but not exported by generic code */
 #endif
-
-#include <asm/processor.h>
-EXPORT_SYMBOL(ia64_cpu_info);
-#ifdef CONFIG_SMP
-EXPORT_SYMBOL(local_per_cpu_offset);
-#endif
-
-#include <asm/uaccess.h>
-EXPORT_SYMBOL(__copy_user);
-EXPORT_SYMBOL(__do_clear_user);
-EXPORT_SYMBOL(__strlen_user);
-EXPORT_SYMBOL(__strncpy_from_user);
-EXPORT_SYMBOL(__strnlen_user);
-
-/* from arch/ia64/lib */
-extern void __divsi3(void);
-extern void __udivsi3(void);
-extern void __modsi3(void);
-extern void __umodsi3(void);
-extern void __divdi3(void);
-extern void __udivdi3(void);
-extern void __moddi3(void);
-extern void __umoddi3(void);
-
-EXPORT_SYMBOL(__divsi3);
-EXPORT_SYMBOL(__udivsi3);
-EXPORT_SYMBOL(__modsi3);
-EXPORT_SYMBOL(__umodsi3);
-EXPORT_SYMBOL(__divdi3);
-EXPORT_SYMBOL(__udivdi3);
-EXPORT_SYMBOL(__moddi3);
-EXPORT_SYMBOL(__umoddi3);
-
-#if defined(CONFIG_MD_RAID456) || defined(CONFIG_MD_RAID456_MODULE)
-extern void xor_ia64_2(void);
-extern void xor_ia64_3(void);
-extern void xor_ia64_4(void);
-extern void xor_ia64_5(void);
-
-EXPORT_SYMBOL(xor_ia64_2);
-EXPORT_SYMBOL(xor_ia64_3);
-EXPORT_SYMBOL(xor_ia64_4);
-EXPORT_SYMBOL(xor_ia64_5);
-#endif
-
-#include <asm/pal.h>
-EXPORT_SYMBOL(ia64_pal_call_phys_stacked);
-EXPORT_SYMBOL(ia64_pal_call_phys_static);
-EXPORT_SYMBOL(ia64_pal_call_stacked);
-EXPORT_SYMBOL(ia64_pal_call_static);
-EXPORT_SYMBOL(ia64_load_scratch_fpregs);
-EXPORT_SYMBOL(ia64_save_scratch_fpregs);
-
-#include <asm/unwind.h>
-EXPORT_SYMBOL(unw_init_running);
-
-#if defined(CONFIG_IA64_ESI) || defined(CONFIG_IA64_ESI_MODULE)
-extern void esi_call_phys (void);
-EXPORT_SYMBOL_GPL(esi_call_phys);
-#endif
-extern char ia64_ivt[];
-EXPORT_SYMBOL(ia64_ivt);
-
-#include <asm/ftrace.h>
-#ifdef CONFIG_FUNCTION_TRACER
-/* mcount is defined in assembly */
-EXPORT_SYMBOL(_mcount);
-#endif
-
-#include <asm/cacheflush.h>
-EXPORT_SYMBOL_GPL(flush_icache_range);
index b1c3cfc93e715b54f485521ed25b36ca8af46b50..44a103a5de2b355278e086abc02b834ea959c743 100644 (file)
@@ -57,6 +57,7 @@
 #include <asm/thread_info.h>
 #include <asm/unistd.h>
 #include <asm/errno.h>
+#include <asm/export.h>
 
 #if 0
 # define PSR_DEFAULT_BITS      psr.ac
@@ -85,6 +86,7 @@
 
        .align 32768    // align on 32KB boundary
        .global ia64_ivt
+       EXPORT_DATA_SYMBOL(ia64_ivt)
 ia64_ivt:
 /////////////////////////////////////////////////////////////////////////////////////////
 // 0x0000 Entry 0 (size 64 bundles) VHPT Translation (8,20,47)
index 0b533441c3c9b2fc434315c23f45dc0924e64b4c..94fb2e3954983b899393beb7636f6341c858fad9 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <asm/asmmacro.h>
 #include <asm/processor.h>
+#include <asm/export.h>
 
        .data
 pal_entry_point:
@@ -87,6 +88,7 @@ GLOBAL_ENTRY(ia64_pal_call_static)
        srlz.d                          // seralize restoration of psr.l
        br.ret.sptk.many b0
 END(ia64_pal_call_static)
+EXPORT_SYMBOL(ia64_pal_call_static)
 
 /*
  * Make a PAL call using the stacked registers calling convention.
@@ -122,6 +124,7 @@ GLOBAL_ENTRY(ia64_pal_call_stacked)
        srlz.d                          // serialize restoration of psr.l
        br.ret.sptk.many b0
 END(ia64_pal_call_stacked)
+EXPORT_SYMBOL(ia64_pal_call_stacked)
 
 /*
  * Make a physical mode PAL call using the static registers calling convention.
@@ -193,6 +196,7 @@ GLOBAL_ENTRY(ia64_pal_call_phys_static)
        srlz.d                          // seralize restoration of psr.l
        br.ret.sptk.many b0
 END(ia64_pal_call_phys_static)
+EXPORT_SYMBOL(ia64_pal_call_phys_static)
 
 /*
  * Make a PAL call using the stacked registers in physical mode.
@@ -250,6 +254,7 @@ GLOBAL_ENTRY(ia64_pal_call_phys_stacked)
        srlz.d                          // seralize restoration of psr.l
        br.ret.sptk.many b0
 END(ia64_pal_call_phys_stacked)
+EXPORT_SYMBOL(ia64_pal_call_phys_stacked)
 
 /*
  * Save scratch fp scratch regs which aren't saved in pt_regs already
@@ -275,6 +280,7 @@ GLOBAL_ENTRY(ia64_save_scratch_fpregs)
        stf.spill [r2]  = f15,32
        br.ret.sptk.many rp
 END(ia64_save_scratch_fpregs)
+EXPORT_SYMBOL(ia64_save_scratch_fpregs)
 
 /*
  * Load scratch fp scratch regs (fp10-fp15)
@@ -296,3 +302,4 @@ GLOBAL_ENTRY(ia64_load_scratch_fpregs)
        ldf.fill  f15 = [r2],32
        br.ret.sptk.many rp
 END(ia64_load_scratch_fpregs)
+EXPORT_SYMBOL(ia64_load_scratch_fpregs)
index 6f54d511cc509a03ac079871b6979f03e6b53bc8..31aa8c0f68e14a284e0f2b088afe4c29de7daee5 100644 (file)
@@ -453,7 +453,7 @@ ia64_peek (struct task_struct *child, struct switch_stack *child_stack,
                        return 0;
                }
        }
-       copied = access_process_vm(child, addr, &ret, sizeof(ret), 0);
+       copied = access_process_vm(child, addr, &ret, sizeof(ret), FOLL_FORCE);
        if (copied != sizeof(ret))
                return -EIO;
        *val = ret;
@@ -489,7 +489,8 @@ ia64_poke (struct task_struct *child, struct switch_stack *child_stack,
                                *ia64_rse_skip_regs(krbs, regnum) = val;
                        }
                }
-       } else if (access_process_vm(child, addr, &val, sizeof(val), 1)
+       } else if (access_process_vm(child, addr, &val, sizeof(val),
+                               FOLL_FORCE | FOLL_WRITE)
                   != sizeof(val))
                return -EIO;
        return 0;
@@ -543,7 +544,8 @@ ia64_sync_user_rbs (struct task_struct *child, struct switch_stack *sw,
                ret = ia64_peek(child, sw, user_rbs_end, addr, &val);
                if (ret < 0)
                        return ret;
-               if (access_process_vm(child, addr, &val, sizeof(val), 1)
+               if (access_process_vm(child, addr, &val, sizeof(val),
+                               FOLL_FORCE | FOLL_WRITE)
                    != sizeof(val))
                        return -EIO;
        }
@@ -559,7 +561,8 @@ ia64_sync_kernel_rbs (struct task_struct *child, struct switch_stack *sw,
 
        /* now copy word for word from user rbs to kernel rbs: */
        for (addr = user_rbs_start; addr < user_rbs_end; addr += 8) {
-               if (access_process_vm(child, addr, &val, sizeof(val), 0)
+               if (access_process_vm(child, addr, &val, sizeof(val),
+                               FOLL_FORCE)
                                != sizeof(val))
                        return -EIO;
 
@@ -1156,7 +1159,8 @@ arch_ptrace (struct task_struct *child, long request,
        case PTRACE_PEEKTEXT:
        case PTRACE_PEEKDATA:
                /* read word at location addr */
-               if (access_process_vm(child, addr, &data, sizeof(data), 0)
+               if (access_process_vm(child, addr, &data, sizeof(data),
+                               FOLL_FORCE)
                    != sizeof(data))
                        return -EIO;
                /* ensure return value is not mistaken for error code */
index afddb3e80a2999bd26dbd309b440a41758345770..7ec7acc844c2e7aa36a1b3ed6acf5c51f5d18f99 100644 (file)
@@ -71,7 +71,11 @@ EXPORT_SYMBOL(__per_cpu_offset);
 #endif
 
 DEFINE_PER_CPU(struct cpuinfo_ia64, ia64_cpu_info);
+EXPORT_SYMBOL(ia64_cpu_info);
 DEFINE_PER_CPU(unsigned long, local_per_cpu_offset);
+#ifdef CONFIG_SMP
+EXPORT_SYMBOL(local_per_cpu_offset);
+#endif
 unsigned long ia64_cycles_per_usec;
 struct ia64_boot_param *ia64_boot_param;
 struct screen_info screen_info;
index 98771e2a78afdb9040c5c05cdcc8b3cfde9fc44b..1f3d3877618fdc934ab20f07695476206fe35e00 100644 (file)
@@ -2,17 +2,15 @@
 # Makefile for ia64-specific library routines..
 #
 
-obj-y := io.o
-
-lib-y := __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o                 \
+lib-y := io.o __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o            \
        __divdi3.o __udivdi3.o __moddi3.o __umoddi3.o                   \
        checksum.o clear_page.o csum_partial_copy.o                     \
        clear_user.o strncpy_from_user.o strlen_user.o strnlen_user.o   \
        flush.o ip_fast_csum.o do_csum.o                                \
        memset.o strlen.o xor.o
 
-obj-$(CONFIG_ITANIUM)  += copy_page.o copy_user.o memcpy.o
-obj-$(CONFIG_MCKINLEY) += copy_page_mck.o memcpy_mck.o
+lib-$(CONFIG_ITANIUM)  += copy_page.o copy_user.o memcpy.o
+lib-$(CONFIG_MCKINLEY) += copy_page_mck.o memcpy_mck.o
 lib-$(CONFIG_PERFMON)  += carta_random.o
 
 AFLAGS___divdi3.o      =
index 2d814e7ed191c60c50ba85de3b8382a6bfc4abc0..3cf5b76e587ff18d4d995b02d31a0ea458d04eaa 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <asm/asmmacro.h>
 #include <asm/page.h>
+#include <asm/export.h>
 
 #ifdef CONFIG_ITANIUM
 # define L3_LINE_SIZE  64      // Itanium L3 line size
@@ -74,3 +75,4 @@ GLOBAL_ENTRY(clear_page)
        mov ar.lc = saved_lc            // restore lc
        br.ret.sptk.many rp
 END(clear_page)
+EXPORT_SYMBOL(clear_page)
index eecd8577b2099c759a21ad486f24d61c831d951f..7b40731ee5d86e13dbac5d49e363912a401eeab6 100644 (file)
@@ -12,6 +12,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 //
 // arguments
@@ -207,3 +208,4 @@ GLOBAL_ENTRY(__do_clear_user)
        mov ar.lc=saved_lc
        br.ret.sptk.many rp
 END(__do_clear_user)
+EXPORT_SYMBOL(__do_clear_user)
index 127d1d050d788212ddf27348304befd7ac7dc4bd..cbdb9e323ffbd5be111a8b7b654633ad264f7112 100644 (file)
@@ -16,6 +16,7 @@
  */
 #include <asm/asmmacro.h>
 #include <asm/page.h>
+#include <asm/export.h>
 
 #define PIPE_DEPTH     3
 #define EPI            p[PIPE_DEPTH-1]
@@ -96,3 +97,4 @@ GLOBAL_ENTRY(copy_page)
        mov ar.lc=saved_lc
        br.ret.sptk.many rp
 END(copy_page)
+EXPORT_SYMBOL(copy_page)
index 3c45d60a81b44789563a9a733fd82ddc15349613..c13f69036876c8ad0bcd191c0afcc76329b13041 100644 (file)
@@ -61,6 +61,7 @@
  */
 #include <asm/asmmacro.h>
 #include <asm/page.h>
+#include <asm/export.h>
 
 #define PREFETCH_DIST  8               // McKinley sustains 16 outstanding L2 misses (8 ld, 8 st)
 
@@ -183,3 +184,4 @@ GLOBAL_ENTRY(copy_page)
        mov pr = saved_pr, -1
        br.ret.sptk.many rp
 END(copy_page)
+EXPORT_SYMBOL(copy_page)
index c952bdc6a09399ee7f511c94212746adf13bd182..66facd52e8d007581c4df4b361532fd9bb28bf80 100644 (file)
@@ -30,6 +30,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 //
 // Tuneable parameters
@@ -608,3 +609,4 @@ GLOBAL_ENTRY(__copy_user)
        mov ar.pfs=saved_pfs
        br.ret.sptk.many rp
 END(__copy_user)
+EXPORT_SYMBOL(__copy_user)
index 1d8c88860063d303c7efa807078326744b55e118..9a5a2f9fad132ef8c59c3aea2e4d0810f06efbc8 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 
        /*
@@ -60,6 +61,7 @@ GLOBAL_ENTRY(flush_icache_range)
        mov     ar.lc=r3                // restore ar.lc
        br.ret.sptk.many rp
 END(flush_icache_range)
+EXPORT_SYMBOL_GPL(flush_icache_range)
 
        /*
         * clflush_cache_range(start,size)
index c91b5b0129ff929e072c80a06e146c529ce18192..715aed79a9ce34e64a66839405c1a2307383cd67 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 #ifdef MODULO
 # define OP    mod
@@ -81,3 +82,4 @@ GLOBAL_ENTRY(NAME)
        getf.sig r8 = f6                // transfer result to result register
        br.ret.sptk.many rp
 END(NAME)
+EXPORT_SYMBOL(NAME)
index 627573c4ceb1972c248eeb6ec881f85f1b79ded2..25840f6977532ab6b2d26aa53976f10bf685287d 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 #ifdef MODULO
 # define OP    mod
@@ -78,3 +79,4 @@ GLOBAL_ENTRY(NAME)
        getf.sig r8 = f11               // transfer result to result register
        br.ret.sptk.many rp
 END(NAME)
+EXPORT_SYMBOL(NAME)
index 620d9dc5220f377c9cd2da899a81795054a27ad0..648e0d4a48390a455caca3da555074af78d33351 100644 (file)
@@ -13,6 +13,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 /*
  * Since we know that most likely this function is called with buf aligned
@@ -92,6 +93,7 @@ GLOBAL_ENTRY(ip_fast_csum)
        mov     b0=r34
        br.ret.sptk.many b0
 END(ip_fast_csum)
+EXPORT_SYMBOL(ip_fast_csum)
 
 GLOBAL_ENTRY(csum_ipv6_magic)
        ld4     r20=[in0],4
@@ -142,3 +144,4 @@ GLOBAL_ENTRY(csum_ipv6_magic)
        andcm   r8=r9,r8
        br.ret.sptk.many b0
 END(csum_ipv6_magic)
+EXPORT_SYMBOL(csum_ipv6_magic)
index 448908d80b6943958d234cc93d07eb5d7f2b49ea..ba172fd6acf4e8571a4fe873b3bf3942a8006769 100644 (file)
@@ -14,6 +14,7 @@
  *     David Mosberger-Tang <davidm@hpl.hp.com>
  */
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 GLOBAL_ENTRY(memcpy)
 
@@ -299,3 +300,4 @@ GLOBAL_ENTRY(memcpy)
        COPY(56, 0)
 
 END(memcpy)
+EXPORT_SYMBOL(memcpy)
index ab0f8763972954117b0ac6a7dc3ac120d6e042c5..b264b6a7967b425b7add3311b542cbb64ca0b41b 100644 (file)
@@ -15,6 +15,7 @@
  */
 #include <asm/asmmacro.h>
 #include <asm/page.h>
+#include <asm/export.h>
 
 #define EK(y...) EX(y)
 
@@ -78,6 +79,7 @@ GLOBAL_ENTRY(memcpy)
        br.cond.sptk .common_code
        ;;
 END(memcpy)
+EXPORT_SYMBOL(memcpy)
 GLOBAL_ENTRY(__copy_user)
        .prologue
 // check dest alignment
@@ -664,3 +666,4 @@ EK(.ex_handler,  (p17)      st8     [dst1]=r39,8);                                          \
 
 /* end of McKinley specific optimization */
 END(__copy_user)
+EXPORT_SYMBOL(__copy_user)
index f26c16aefb1cbfdec5cd2fa5e466ae5426c627ca..87b974704075f1f5d5570260c09e4fddf74b0e7d 100644 (file)
@@ -18,6 +18,7 @@
    to get peak speed when value = 0.  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 #undef ret
 
 #define dest           in0
@@ -360,3 +361,4 @@ GLOBAL_ENTRY(memset)
        br.ret.sptk.many rp
 }
 END(memset)
+EXPORT_SYMBOL(memset)
index e0cdac0a85b873a1c714c4d4f0c8ca5c40aca015..1a6e17c657b4219e902fd1d2dc5472deebdf75de 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 //
 //
@@ -190,3 +191,4 @@ GLOBAL_ENTRY(strlen)
        mov ar.pfs=saved_pfs    // because of ar.ec, restore no matter what
        br.ret.sptk.many rp     // end of successful recovery code
 END(strlen)
+EXPORT_SYMBOL(strlen)
index c71eded4285efb49e5994ef51b163192b15327fd..9d257684e733461c4ac4f5d2b56638f2cab48791 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 //
 // int strlen_user(char *)
@@ -196,3 +197,4 @@ GLOBAL_ENTRY(__strlen_user)
        mov ar.pfs=saved_pfs    // because of ar.ec, restore no matter what
        br.ret.sptk.many rp
 END(__strlen_user)
+EXPORT_SYMBOL(__strlen_user)
index a504381f31ebe7222dc115c9ad4f3ab5c37241b8..ca9ccf280e2e5c634938cad13ea759c7fae8c7d2 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 GLOBAL_ENTRY(__strncpy_from_user)
        alloc r2=ar.pfs,3,0,0,0
@@ -42,3 +43,4 @@ GLOBAL_ENTRY(__strncpy_from_user)
 [.Lexit:]
        br.ret.sptk.many rp
 END(__strncpy_from_user)
+EXPORT_SYMBOL(__strncpy_from_user)
index d09066b1e49d31dd242561aa54f632f5dda20a6c..80a5dfd1d402b9d28581315f5a59424fc149d7ce 100644 (file)
@@ -13,6 +13,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 GLOBAL_ENTRY(__strnlen_user)
        .prologue
@@ -43,3 +44,4 @@ GLOBAL_ENTRY(__strnlen_user)
        mov ar.lc=r16                   // restore ar.lc
        br.ret.sptk.many rp
 END(__strnlen_user)
+EXPORT_SYMBOL(__strnlen_user)
index 54e3f7eab8e9aecfa95c5df8378b7d7c22f455ab..c83f1c410691b7c2c9b47f563bf6fb5741633c45 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 GLOBAL_ENTRY(xor_ia64_2)
        .prologue
@@ -51,6 +52,7 @@ GLOBAL_ENTRY(xor_ia64_2)
        mov pr = r29, -1
        br.ret.sptk.few rp
 END(xor_ia64_2)
+EXPORT_SYMBOL(xor_ia64_2)
 
 GLOBAL_ENTRY(xor_ia64_3)
        .prologue
@@ -91,6 +93,7 @@ GLOBAL_ENTRY(xor_ia64_3)
        mov pr = r29, -1
        br.ret.sptk.few rp
 END(xor_ia64_3)
+EXPORT_SYMBOL(xor_ia64_3)
 
 GLOBAL_ENTRY(xor_ia64_4)
        .prologue
@@ -134,6 +137,7 @@ GLOBAL_ENTRY(xor_ia64_4)
        mov pr = r29, -1
        br.ret.sptk.few rp
 END(xor_ia64_4)
+EXPORT_SYMBOL(xor_ia64_4)
 
 GLOBAL_ENTRY(xor_ia64_5)
        .prologue
@@ -182,3 +186,4 @@ GLOBAL_ENTRY(xor_ia64_5)
        mov pr = r29, -1
        br.ret.sptk.few rp
 END(xor_ia64_5)
+EXPORT_SYMBOL(xor_ia64_5)
index 51f5e9aa49016fdce8112eb72083c0b167f21df8..c145605a981ff4fbc441ffe4512a6167147f6bec 100644 (file)
@@ -493,7 +493,8 @@ unregister_all_debug_traps(struct task_struct *child)
        int i;
 
        for (i = 0; i < p->nr_trap; i++)
-               access_process_vm(child, p->addr[i], &p->insn[i], sizeof(p->insn[i]), 1);
+               access_process_vm(child, p->addr[i], &p->insn[i], sizeof(p->insn[i]),
+                               FOLL_FORCE | FOLL_WRITE);
        p->nr_trap = 0;
 }
 
@@ -537,7 +538,8 @@ embed_debug_trap(struct task_struct *child, unsigned long next_pc)
        unsigned long next_insn, code;
        unsigned long addr = next_pc & ~3;
 
-       if (access_process_vm(child, addr, &next_insn, sizeof(next_insn), 0)
+       if (access_process_vm(child, addr, &next_insn, sizeof(next_insn),
+                       FOLL_FORCE)
            != sizeof(next_insn)) {
                return -1; /* error */
        }
@@ -546,7 +548,8 @@ embed_debug_trap(struct task_struct *child, unsigned long next_pc)
        if (register_debug_trap(child, next_pc, next_insn, &code)) {
                return -1; /* error */
        }
-       if (access_process_vm(child, addr, &code, sizeof(code), 1)
+       if (access_process_vm(child, addr, &code, sizeof(code),
+                       FOLL_FORCE | FOLL_WRITE)
            != sizeof(code)) {
                return -1; /* error */
        }
@@ -562,7 +565,8 @@ withdraw_debug_trap(struct pt_regs *regs)
        addr = (regs->bpc - 2) & ~3;
        regs->bpc -= 2;
        if (unregister_debug_trap(current, addr, &code)) {
-           access_process_vm(current, addr, &code, sizeof(code), 1);
+           access_process_vm(current, addr, &code, sizeof(code),
+                   FOLL_FORCE | FOLL_WRITE);
            invalidate_cache();
        }
 }
@@ -589,7 +593,8 @@ void user_enable_single_step(struct task_struct *child)
        /* Compute next pc.  */
        pc = get_stack_long(child, PT_BPC);
 
-       if (access_process_vm(child, pc&~3, &insn, sizeof(insn), 0)
+       if (access_process_vm(child, pc&~3, &insn, sizeof(insn),
+                       FOLL_FORCE)
            != sizeof(insn))
                return;
 
diff --git a/arch/m68k/include/asm/export.h b/arch/m68k/include/asm/export.h
new file mode 100644 (file)
index 0000000..0af20f4
--- /dev/null
@@ -0,0 +1,3 @@
+#define KSYM_ALIGN 2
+#define KCRC_ALIGN 2
+#include <asm-generic/export.h>
index 8a1c4d3f91c8a78bf7694a2579d20db92ed7eba8..74c898ced8cc0481e3fa70dc0b7e6d8b86d1994f 100644 (file)
@@ -13,7 +13,7 @@ extra-$(CONFIG_SUN3X) := head.o
 extra-$(CONFIG_SUN3)   := sun3-head.o
 extra-y                        += vmlinux.lds
 
-obj-y  := entry.o irq.o m68k_ksyms.o module.o process.o ptrace.o
+obj-y  := entry.o irq.o module.o process.o ptrace.o
 obj-y  += setup.o signal.o sys_m68k.o syscalltable.o time.o traps.o
 
 obj-$(CONFIG_MMU_MOTOROLA) += ints.o vectors.o
diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c
deleted file mode 100644 (file)
index 774c1bd..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#include <linux/module.h>
-
-asmlinkage long long __ashldi3 (long long, int);
-asmlinkage long long __ashrdi3 (long long, int);
-asmlinkage long long __lshrdi3 (long long, int);
-asmlinkage long long __muldi3 (long long, long long);
-
-/* The following are special because they're not called
-   explicitly (the C compiler generates them).  Fortunately,
-   their interface isn't gonna change any time soon now, so
-   it's OK to leave it out of version control.  */
-EXPORT_SYMBOL(__ashldi3);
-EXPORT_SYMBOL(__ashrdi3);
-EXPORT_SYMBOL(__lshrdi3);
-EXPORT_SYMBOL(__muldi3);
-
-#if defined(CONFIG_CPU_HAS_NO_MULDIV64)
-/*
- * Simpler 68k and ColdFire parts also need a few other gcc functions.
- */
-extern long long __divsi3(long long, long long);
-extern long long __modsi3(long long, long long);
-extern long long __mulsi3(long long, long long);
-extern long long __udivsi3(long long, long long);
-extern long long __umodsi3(long long, long long);
-
-EXPORT_SYMBOL(__divsi3);
-EXPORT_SYMBOL(__modsi3);
-EXPORT_SYMBOL(__mulsi3);
-EXPORT_SYMBOL(__udivsi3);
-EXPORT_SYMBOL(__umodsi3);
-#endif
index 37234c2df47f6e8ed6edd4eb5d60fc29178f8ef7..8dffd36ec4f2411f867629111844c907ac47f7bf 100644 (file)
@@ -13,6 +13,9 @@ 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/compiler.h>
+#include <linux/export.h>
+
 #define BITS_PER_UNIT 8
 
 typedef                 int SItype     __attribute__ ((mode (SI)));
@@ -55,3 +58,4 @@ __ashldi3 (DItype u, word_type b)
 
   return w.ll;
 }
+EXPORT_SYMBOL(__ashldi3);
index 1d59345f36c631550767bc8f8c77c9e6a69afbc0..e6565a3ee2c37065949cd92eed8deeb78c709f33 100644 (file)
@@ -13,6 +13,9 @@ 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/compiler.h>
+#include <linux/export.h>
+
 #define BITS_PER_UNIT 8
 
 typedef                 int SItype     __attribute__ ((mode (SI)));
@@ -56,3 +59,4 @@ __ashrdi3 (DItype u, word_type b)
 
   return w.ll;
 }
+EXPORT_SYMBOL(__ashrdi3);
index 2c0ec85ac661547382c2206b613e86ba9d3a63be..3a2143f51631a0e6a819388f53ee5a910a676917 100644 (file)
@@ -33,6 +33,8 @@ General Public License for more details. */
    D. V. Henkel-Wallace (gumby@cygnus.com) Fete Bastille, 1992
 */
 
+#include <asm/export.h>
+
 /* These are predefined by new versions of GNU cpp.  */
 
 #ifndef __USER_LABEL_PREFIX__
@@ -118,3 +120,4 @@ L2: movel   d1, sp@-
 L3:    movel   sp@+, d2
        rts
 
+       EXPORT_SYMBOL(__divsi3)
index 49e1ec8f2cc27a9f9880bfe78cef279afcdf3ab1..039779737c7d28bfb1d8caf29995fa440c3f2a2a 100644 (file)
@@ -13,6 +13,9 @@ 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/compiler.h>
+#include <linux/export.h>
+
 #define BITS_PER_UNIT 8
 
 typedef                 int SItype     __attribute__ ((mode (SI)));
@@ -55,3 +58,4 @@ __lshrdi3 (DItype u, word_type b)
 
   return w.ll;
 }
+EXPORT_SYMBOL(__lshrdi3);
index 1d9e0efdf31d201f67ae56496a6fae6496ef8868..1c967649a4e0e07c47331c8bb228d97602756ac9 100644 (file)
@@ -33,6 +33,8 @@ General Public License for more details. */
    D. V. Henkel-Wallace (gumby@cygnus.com) Fete Bastille, 1992
 */
 
+#include <asm/export.h>
+
 /* These are predefined by new versions of GNU cpp.  */
 
 #ifndef __USER_LABEL_PREFIX__
@@ -106,3 +108,4 @@ SYM (__modsi3):
        movel   d1, d0
        rts
 
+       EXPORT_SYMBOL(__modsi3)
index 9006d15b87218d95d77effd305bb49b4968e4914..6459af5b2af0a9c312c8c04d1dfc2009e24c30a6 100644 (file)
@@ -14,6 +14,9 @@ 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/compiler.h>
+#include <linux/export.h>
+
 #ifdef CONFIG_CPU_HAS_NO_MULDIV64
 
 #define SI_TYPE_SIZE 32
@@ -90,3 +93,4 @@ __muldi3 (DItype u, DItype v)
 
   return w.ll;
 }
+EXPORT_SYMBOL(__muldi3);
index c39ad4e738e9a6522fcef640020502f727d46a52..855675e69a8a2bb50e1c45e728ff2a218732a893 100644 (file)
@@ -32,7 +32,7 @@ General Public License for more details. */
    Some of this code comes from MINIX, via the folks at ericsson.
    D. V. Henkel-Wallace (gumby@cygnus.com) Fete Bastille, 1992
 */
-
+#include <asm/export.h>
 /* These are predefined by new versions of GNU cpp.  */
 
 #ifndef __USER_LABEL_PREFIX__
@@ -102,4 +102,4 @@ SYM (__mulsi3):
        addl    d1, d0
 
        rts
-
+       EXPORT_SYMBOL(__mulsi3)
index 35a5446572a5ee3bae78e725ae8c39619332b7a0..78440ae513bf318e34a6463494c9c7dcfea48bf7 100644 (file)
@@ -32,7 +32,7 @@ General Public License for more details. */
    Some of this code comes from MINIX, via the folks at ericsson.
    D. V. Henkel-Wallace (gumby@cygnus.com) Fete Bastille, 1992
 */
-
+#include <asm/export.h>
 /* These are predefined by new versions of GNU cpp.  */
 
 #ifndef __USER_LABEL_PREFIX__
@@ -154,4 +154,4 @@ L2: subql   IMM (1),d4
        unlk    a6              | and return
        rts
 #endif /* __mcf5200__ || __mcoldfire__ */
-
+       EXPORT_SYMBOL(__udivsi3)
index 099da514a8fd80daa85d3ba2644e3f2792175b5b..b6fd11f58948debdce515dd7114ca256540fb776 100644 (file)
@@ -32,7 +32,7 @@ General Public License for more details. */
    Some of this code comes from MINIX, via the folks at ericsson.
    D. V. Henkel-Wallace (gumby@cygnus.com) Fete Bastille, 1992
 */
-
+#include <asm/export.h>
 /* These are predefined by new versions of GNU cpp.  */
 
 #ifndef __USER_LABEL_PREFIX__
@@ -105,4 +105,4 @@ SYM (__umodsi3):
        subl    d0, d1          /* d1 = a - (a/b)*b */
        movel   d1, d0
        rts
-
+       EXPORT_SYMBOL(__umodsi3)
index 470e365f04ea4ee3f4503c060e8dacbd1e9dd0da..8ff0a70865f65cc9f9f71d9b9f126dd7fb820793 100644 (file)
 #define atomic_dec(v) atomic_sub(1, (v))
 
 #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
+#define atomic_dec_if_positive(v)       atomic_sub_if_positive(1, v)
 
 #endif
 
-#define atomic_dec_if_positive(v)       atomic_sub_if_positive(1, v)
-
 #include <asm-generic/atomic64.h>
 
 #endif /* __ASM_METAG_ATOMIC_H */
index c5cd63a4b6d53b399e91e8ebad7358a3f45fce8c..f5f1bdb292de0e9445e4caad4653927b2ef1f576 100644 (file)
@@ -11,6 +11,7 @@ platforms += cavium-octeon
 platforms += cobalt
 platforms += dec
 platforms += emma
+platforms += generic
 platforms += jazz
 platforms += jz4740
 platforms += lantiq
@@ -18,7 +19,6 @@ platforms += lasat
 platforms += loongson32
 platforms += loongson64
 platforms += mti-malta
-platforms += mti-sead3
 platforms += netlogic
 platforms += paravirt
 platforms += pic32
index 1a322c807f220e44c735650ced5142c8007366d9..b3c5bde43d34f85afc50c8eb955745b88836386c 100644 (file)
@@ -65,6 +65,7 @@ config MIPS
        select HANDLE_DOMAIN_IRQ
        select HAVE_EXIT_THREAD
        select HAVE_REGS_AND_STACK_ACCESS_API
+       select HAVE_ARCH_HARDENED_USERCOPY
 
 menu "Machine selection"
 
@@ -72,6 +73,57 @@ choice
        prompt "System type"
        default SGI_IP22
 
+config MIPS_GENERIC
+       bool "Generic board-agnostic MIPS kernel"
+       select BOOT_RAW
+       select BUILTIN_DTB
+       select CEVT_R4K
+       select CLKSRC_MIPS_GIC
+       select COMMON_CLK
+       select CPU_MIPSR2_IRQ_VI
+       select CPU_MIPSR2_IRQ_EI
+       select CSRC_R4K
+       select DMA_PERDEV_COHERENT
+       select HW_HAS_PCI
+       select IRQ_MIPS_CPU
+       select LIBFDT
+       select MIPS_CPU_SCACHE
+       select MIPS_GIC
+       select MIPS_L1_CACHE_SHIFT_7
+       select NO_EXCEPT_FILL
+       select PCI_DRIVERS_GENERIC
+       select PINCTRL
+       select SMP_UP if SMP
+       select SYS_HAS_CPU_MIPS32_R1
+       select SYS_HAS_CPU_MIPS32_R2
+       select SYS_HAS_CPU_MIPS32_R6
+       select SYS_HAS_CPU_MIPS64_R1
+       select SYS_HAS_CPU_MIPS64_R2
+       select SYS_HAS_CPU_MIPS64_R6
+       select SYS_SUPPORTS_32BIT_KERNEL
+       select SYS_SUPPORTS_64BIT_KERNEL
+       select SYS_SUPPORTS_BIG_ENDIAN
+       select SYS_SUPPORTS_HIGHMEM
+       select SYS_SUPPORTS_LITTLE_ENDIAN
+       select SYS_SUPPORTS_MICROMIPS
+       select SYS_SUPPORTS_MIPS_CPS
+       select SYS_SUPPORTS_MIPS16
+       select SYS_SUPPORTS_MULTITHREADING
+       select SYS_SUPPORTS_RELOCATABLE
+       select SYS_SUPPORTS_SMARTMIPS
+       select USB_EHCI_BIG_ENDIAN_DESC if BIG_ENDIAN
+       select USB_EHCI_BIG_ENDIAN_MMIO if BIG_ENDIAN
+       select USB_OHCI_BIG_ENDIAN_DESC if BIG_ENDIAN
+       select USB_OHCI_BIG_ENDIAN_MMIO if BIG_ENDIAN
+       select USB_UHCI_BIG_ENDIAN_DESC if BIG_ENDIAN
+       select USB_UHCI_BIG_ENDIAN_MMIO if BIG_ENDIAN
+       select USE_OF
+       help
+         Select this to build a kernel which aims to support multiple boards,
+         generally using a flattened device tree passed from the bootloader
+         using the boot protocol defined in the UHI (Unified Hosting
+         Interface) specification.
+
 config MIPS_ALCHEMY
        bool "Alchemy processor based machines"
        select ARCH_PHYS_ADDR_T_64BIT
@@ -478,6 +530,7 @@ config MIPS_MALTA
        select SYS_SUPPORTS_ZBOOT
        select SYS_SUPPORTS_RELOCATABLE
        select USE_OF
+       select LIBFDT
        select ZONE_DMA32 if 64BIT
        select BUILTIN_DTB
        select LIBFDT
@@ -493,42 +546,6 @@ config MACH_PIC32
          Microchip PIC32 is a family of general-purpose 32 bit MIPS core
          microcontrollers.
 
-config MIPS_SEAD3
-       bool "MIPS SEAD3 board"
-       select BOOT_ELF32
-       select BOOT_RAW
-       select BUILTIN_DTB
-       select CEVT_R4K
-       select CSRC_R4K
-       select CLKSRC_MIPS_GIC
-       select COMMON_CLK
-       select CPU_MIPSR2_IRQ_VI
-       select CPU_MIPSR2_IRQ_EI
-       select DMA_NONCOHERENT
-       select IRQ_MIPS_CPU
-       select MIPS_GIC
-       select LIBFDT
-       select MIPS_MSC
-       select SYS_HAS_CPU_MIPS32_R1
-       select SYS_HAS_CPU_MIPS32_R2
-       select SYS_HAS_CPU_MIPS32_R6
-       select SYS_HAS_CPU_MIPS64_R1
-       select SYS_HAS_EARLY_PRINTK
-       select SYS_SUPPORTS_32BIT_KERNEL
-       select SYS_SUPPORTS_64BIT_KERNEL
-       select SYS_SUPPORTS_BIG_ENDIAN
-       select SYS_SUPPORTS_LITTLE_ENDIAN
-       select SYS_SUPPORTS_SMARTMIPS
-       select SYS_SUPPORTS_MICROMIPS
-       select SYS_SUPPORTS_MIPS16
-       select SYS_SUPPORTS_RELOCATABLE
-       select USB_EHCI_BIG_ENDIAN_DESC
-       select USB_EHCI_BIG_ENDIAN_MMIO
-       select USE_OF
-       help
-         This enables support for the MIPS Technologies SEAD3 evaluation
-         board.
-
 config NEC_MARKEINS
        bool "NEC EMMA2RH Mark-eins board"
        select SOC_EMMA2RH
@@ -988,6 +1005,7 @@ source "arch/mips/ath79/Kconfig"
 source "arch/mips/bcm47xx/Kconfig"
 source "arch/mips/bcm63xx/Kconfig"
 source "arch/mips/bmips/Kconfig"
+source "arch/mips/generic/Kconfig"
 source "arch/mips/jazz/Kconfig"
 source "arch/mips/jz4740/Kconfig"
 source "arch/mips/lantiq/Kconfig"
@@ -1098,6 +1116,10 @@ config DMA_MAYBE_COHERENT
        select DMA_NONCOHERENT
        bool
 
+config DMA_PERDEV_COHERENT
+       bool
+       select DMA_MAYBE_COHERENT
+
 config DMA_COHERENT
        bool
 
@@ -1401,6 +1423,16 @@ config CPU_LOONGSON1B
          The Loongson 1B is a 32-bit SoC, which implements the MIPS32
          release 2 instruction set.
 
+config CPU_LOONGSON1C
+       bool "Loongson 1C"
+       depends on SYS_HAS_CPU_LOONGSON1C
+       select CPU_LOONGSON1
+       select ARCH_WANT_OPTIONAL_GPIOLIB
+       select LEDS_GPIO_REGISTER
+       help
+         The Loongson 1C is a 32-bit SoC, which implements the MIPS32
+         release 2 instruction set.
+
 config CPU_MIPS32_R1
        bool "MIPS32 Release 1"
        depends on SYS_HAS_CPU_MIPS32_R1
@@ -1850,6 +1882,9 @@ config SYS_HAS_CPU_LOONGSON2F
 config SYS_HAS_CPU_LOONGSON1B
        bool
 
+config SYS_HAS_CPU_LOONGSON1C
+       bool
+
 config SYS_HAS_CPU_MIPS32_R1
        bool
 
@@ -2906,7 +2941,7 @@ endchoice
 choice
        prompt "Kernel command line type" if !CMDLINE_OVERRIDE
        default MIPS_CMDLINE_FROM_DTB if USE_OF && !ATH79 && !MACH_INGENIC && \
-                                        !MIPS_MALTA && !MIPS_SEAD3 && \
+                                        !MIPS_MALTA && \
                                         !CAVIUM_OCTEON_SOC
        default MIPS_CMDLINE_FROM_BOOTLOADER
 
@@ -2960,7 +2995,6 @@ config PCI
        bool "Support for PCI controller"
        depends on HW_HAS_PCI
        select PCI_DOMAINS
-       select NO_GENERIC_PCI_IOPORT_MAP
        help
          Find out whether you have a PCI motherboard. PCI is the name of a
          bus system, i.e. the way the CPU talks to the other stuff inside
@@ -2981,6 +3015,17 @@ config HT_PCI
 config PCI_DOMAINS
        bool
 
+config PCI_DOMAINS_GENERIC
+       bool
+
+config PCI_DRIVERS_GENERIC
+       select PCI_DOMAINS_GENERIC if PCI_DOMAINS
+       bool
+
+config PCI_DRIVERS_LEGACY
+       def_bool !PCI_DRIVERS_GENERIC
+       select NO_GENERIC_PCI_IOPORT_MAP
+
 source "drivers/pci/Kconfig"
 
 #
index 598ab2930fce67bb373827d7bbb09be35880a75e..fbf40d3c81239e684a6f96856d19bf0ea359d4ac 100644 (file)
@@ -262,7 +262,14 @@ KBUILD_CPPFLAGS += -DVMLINUX_LOAD_ADDRESS=$(load-y)
 KBUILD_CPPFLAGS += -DDATAOFFSET=$(if $(dataoffset-y),$(dataoffset-y),0)
 
 bootvars-y     = VMLINUX_LOAD_ADDRESS=$(load-y) \
-                 VMLINUX_ENTRY_ADDRESS=$(entry-y)
+                 VMLINUX_ENTRY_ADDRESS=$(entry-y) \
+                 PLATFORM=$(platform-y)
+ifdef CONFIG_32BIT
+bootvars-y     += ADDR_BITS=32
+endif
+ifdef CONFIG_64BIT
+bootvars-y     += ADDR_BITS=64
+endif
 
 LDFLAGS                        += -m $(ld-emul)
 
@@ -302,6 +309,11 @@ boot-y                     += uImage.gz
 boot-y                 += uImage.lzma
 boot-y                 += uImage.lzo
 endif
+boot-y                 += vmlinux.itb
+boot-y                 += vmlinux.gz.itb
+boot-y                 += vmlinux.bz2.itb
+boot-y                 += vmlinux.lzma.itb
+boot-y                 += vmlinux.lzo.itb
 
 # compressed boot image targets (arch/mips/boot/compressed/)
 bootz-y                        := vmlinuz
@@ -425,4 +437,67 @@ define archhelp
        echo '  dtbs_install         - Install dtbs to $(INSTALL_DTBS_PATH)'
        echo
        echo '  These will be default as appropriate for a configured platform.'
+       echo
+       echo '  If you are targeting a system supported by generic kernels you may'
+       echo '  configure the kernel for a given architecture target like so:'
+       echo
+       echo '  {micro32,32,64}{r1,r2,r6}{el,}_defconfig <BOARDS="list of boards">'
+       echo
+       echo '  Otherwise, the following default configurations are available:'
 endef
+
+generic_config_dir = $(srctree)/arch/$(ARCH)/configs/generic
+generic_defconfigs :=
+
+#
+# If the user generates a generic kernel configuration without specifying a
+# list of boards to include the config fragments for, default to including all
+# available board config fragments.
+#
+ifeq ($(BOARDS),)
+BOARDS = $(patsubst board-%.config,%,$(notdir $(wildcard $(generic_config_dir)/board-*.config)))
+endif
+
+#
+# Generic kernel configurations which merge generic_defconfig with the
+# appropriate config fragments from arch/mips/configs/generic/, resulting in
+# the ability to easily configure the kernel for a given architecture,
+# endianness & set of boards without duplicating the needed configuration in
+# hundreds of defconfig files.
+#
+define gen_generic_defconfigs
+$(foreach bits,$(1),$(foreach rev,$(2),$(foreach endian,$(3),
+target := $(bits)$(rev)$(filter el,$(endian))_defconfig
+generic_defconfigs += $$(target)
+$$(target): $(generic_config_dir)/$(bits)$(rev).config
+$$(target): $(generic_config_dir)/$(endian).config
+)))
+endef
+
+$(eval $(call gen_generic_defconfigs,32 64,r1 r2 r6,eb el))
+$(eval $(call gen_generic_defconfigs,micro32,r2,eb el))
+
+.PHONY: $(generic_defconfigs)
+$(generic_defconfigs):
+       $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh \
+               -m -O $(objtree) $(srctree)/arch/$(ARCH)/configs/generic_defconfig $^ \
+               $(foreach board,$(BOARDS),$(generic_config_dir)/board-$(board).config)
+       $(Q)$(MAKE) olddefconfig
+
+#
+# Prevent generic merge_config rules attempting to merge single fragments
+#
+$(generic_config_dir)/%.config: ;
+
+#
+# Legacy defconfig compatibility - these targets used to be real defconfigs but
+# now that the boards have been converted to use the generic kernel they are
+# wrappers around the generic rules above.
+#
+.PHONY: sead3_defconfig
+sead3_defconfig:
+       $(Q)$(MAKE) 32r2el_defconfig BOARDS=sead-3
+
+.PHONY: sead3micro_defconfig
+sead3micro_defconfig:
+       $(Q)$(MAKE) micro32r2el_defconfig BOARDS=sead-3
index 2902138b3e0f56f639896c571e5bc87172f74d2f..7faaa6d593a74c119cfe6523e4c3e29009b8116a 100644 (file)
@@ -48,17 +48,17 @@ void __init plat_mem_setup(void)
                clear_c0_config(1 << 19); /* Clear Config[OD] */
 
        hw_coherentio = 0;
-       coherentio = 1;
+       coherentio = IO_COHERENCE_ENABLED;
        switch (alchemy_get_cputype()) {
        case ALCHEMY_CPU_AU1000:
        case ALCHEMY_CPU_AU1500:
        case ALCHEMY_CPU_AU1100:
-               coherentio = 0;
+               coherentio = IO_COHERENCE_DISABLED;
                break;
        case ALCHEMY_CPU_AU1200:
                /* Au1200 AB USB does not support coherent memory */
                if (0 == (read_c0_prid() & PRID_REV_MASK))
-                       coherentio = 0;
+                       coherentio = IO_COHERENCE_DISABLED;
                break;
        }
 
index df761d38f7fc989bd0ec51df191661a35278e64d..e3c9872a4aa5d317ba3cdcf060a52586aeea5203 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * 8250 UART probe driver for the BCM47XX platforms
+ * Author: Aurelien Jarno
+ *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
@@ -6,7 +9,6 @@
  * Copyright (C) 2007 Aurelien Jarno <aurelien@aurel32.net>
  */
 
-#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/serial.h>
 #include <linux/serial_8250.h>
@@ -88,9 +90,4 @@ static int __init uart8250_init(void)
        }
        return -EINVAL;
 }
-
-module_init(uart8250_init);
-
-MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("8250 UART probe driver for the BCM47XX platforms");
+device_initcall(uart8250_init);
index 637565284732d396354b5a1eb056bb74e2703380..b49fc9cb9cad2de2c3768ba93e54da41177ff0a7 100644 (file)
@@ -326,6 +326,9 @@ EXPORT_SYMBOL(clk_enable);
 
 void clk_disable(struct clk *clk)
 {
+       if (!clk)
+               return;
+
        mutex_lock(&clocks_mutex);
        clk_disable_unlocked(clk);
        mutex_unlock(&clocks_mutex);
index 264328d528c72e40ad12c6b8bbff2bbb9ccda742..2d60f25403de1128833a2b525061acf97c46cfbe 100644 (file)
@@ -21,10 +21,6 @@ config DT_BCM93384WVG_VIPER
        bool "BCM93384WVG Viper CPU (EXPERIMENTAL)"
        select BUILTIN_DTB
 
-config DT_BCM96358NB4SER
-       bool "BCM96358NB4SER"
-       select BUILTIN_DTB
-
 config DT_BCM96368MVWG
        bool "BCM96368MVWG"
        select BUILTIN_DTB
@@ -65,6 +61,22 @@ config DT_BCM97435SVMB
        bool "BCM97435SVMB"
        select BUILTIN_DTB
 
+config DT_COMTREND_VR3032U
+       bool "Comtrend VR-3032u"
+       select BUILTIN_DTB
+
+config DT_NETGEAR_CVG834G
+       bool "NETGEAR CVG834G"
+       select BUILTIN_DTB
+
+config DT_SFR_NEUFBOX4_SERCOMM
+       bool "SFR Neufbox 4 (Sercomm)"
+       select BUILTIN_DTB
+
+config DT_SFR_NEUFBOX6_SERCOMM
+       bool "SFR Neufbox 6 (Sercomm)"
+       select BUILTIN_DTB
+
 endchoice
 
 endif
index 6776042679dd263a9c862e79942946afaff9a37e..3b6f687f177cdf5b3826e10978a1bd465ed2a96e 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/of.h>
 #include <linux/of_fdt.h>
 #include <linux/of_platform.h>
+#include <linux/libfdt.h>
 #include <linux/smp.h>
 #include <asm/addrspace.h>
 #include <asm/bmips.h>
@@ -98,7 +99,7 @@ static void bcm6328_quirks(void)
 static void bcm6358_quirks(void)
 {
        /*
-        * BCM6358 needs special handling for its shared TLB, so
+        * BCM3368/BCM6358 need special handling for their shared TLB, so
         * disable SMP for now
         */
        bmips_smp_enabled = 0;
@@ -110,10 +111,12 @@ static void bcm6368_quirks(void)
 }
 
 static const struct bmips_quirk bmips_quirk_list[] = {
+       { "brcm,bcm3368",               &bcm6358_quirks                 },
        { "brcm,bcm3384-viper",         &bcm3384_viper_quirks           },
        { "brcm,bcm33843-viper",        &bcm3384_viper_quirks           },
        { "brcm,bcm6328",               &bcm6328_quirks                 },
        { "brcm,bcm6358",               &bcm6358_quirks                 },
+       { "brcm,bcm6362",               &bcm6368_quirks                 },
        { "brcm,bcm6368",               &bcm6368_quirks                 },
        { "brcm,bcm63168",              &bcm6368_quirks                 },
        { "brcm,bcm63268",              &bcm6368_quirks                 },
@@ -150,6 +153,8 @@ void __init plat_time_init(void)
        mips_hpt_frequency = freq;
 }
 
+extern const char __appended_dtb;
+
 void __init plat_mem_setup(void)
 {
        void *dtb;
@@ -159,6 +164,11 @@ void __init plat_mem_setup(void)
        ioport_resource.start = 0;
        ioport_resource.end = ~0;
 
+#ifdef CONFIG_MIPS_ELF_APPENDED_DTB
+       if (!fdt_check_header(&__appended_dtb))
+               dtb = (void *)&__appended_dtb;
+       else
+#endif
        /* intended to somewhat resemble ARM; see Documentation/arm/Booting */
        if (fw_arg0 == 0 && fw_arg1 == 0xffffffff)
                dtb = phys_to_virt(fw_arg2);
index acb1988f354edc58072399a076656c0f2ffd149e..2728a9a9c7c5bc4f822ab6788e1f889cb39c0a51 100644 (file)
@@ -100,3 +100,69 @@ $(obj)/uImage.lzo: $(obj)/vmlinux.bin.lzo FORCE
 $(obj)/uImage: $(obj)/uImage.$(suffix-y)
        @ln -sf $(notdir $<) $@
        @echo '  Image $@ is ready'
+
+#
+# Flattened Image Tree (.itb) images
+#
+
+targets += vmlinux.itb
+targets += vmlinux.gz.itb
+targets += vmlinux.bz2.itb
+targets += vmlinux.lzma.itb
+targets += vmlinux.lzo.itb
+
+ifeq ($(ADDR_BITS),32)
+       itb_addr_cells = 1
+endif
+ifeq ($(ADDR_BITS),64)
+       itb_addr_cells = 2
+endif
+
+quiet_cmd_cpp_its_S = ITS     $@
+      cmd_cpp_its_S = $(CPP) $(cpp_flags) -P -C -o $@ $< \
+                       -DKERNEL_NAME="\"Linux $(KERNELRELEASE)\"" \
+                       -DVMLINUX_BINARY="\"$(3)\"" \
+                       -DVMLINUX_COMPRESSION="\"$(2)\"" \
+                       -DVMLINUX_LOAD_ADDRESS=$(VMLINUX_LOAD_ADDRESS) \
+                       -DVMLINUX_ENTRY_ADDRESS=$(VMLINUX_ENTRY_ADDRESS) \
+                       -DADDR_BITS=$(ADDR_BITS) \
+                       -DADDR_CELLS=$(itb_addr_cells)
+
+$(obj)/vmlinux.its: $(srctree)/arch/mips/$(PLATFORM)/vmlinux.its.S FORCE
+       $(call if_changed_dep,cpp_its_S,none,vmlinux.bin)
+
+$(obj)/vmlinux.gz.its: $(srctree)/arch/mips/$(PLATFORM)/vmlinux.its.S FORCE
+       $(call if_changed_dep,cpp_its_S,gzip,vmlinux.bin.gz)
+
+$(obj)/vmlinux.bz2.its: $(srctree)/arch/mips/$(PLATFORM)/vmlinux.its.S FORCE
+       $(call if_changed_dep,cpp_its_S,bzip2,vmlinux.bin.bz2)
+
+$(obj)/vmlinux.lzma.its: $(srctree)/arch/mips/$(PLATFORM)/vmlinux.its.S FORCE
+       $(call if_changed_dep,cpp_its_S,lzma,vmlinux.bin.lzma)
+
+$(obj)/vmlinux.lzo.its: $(srctree)/arch/mips/$(PLATFORM)/vmlinux.its.S FORCE
+       $(call if_changed_dep,cpp_its_S,lzo,vmlinux.bin.lzo)
+
+quiet_cmd_itb-image = ITB     $@
+      cmd_itb-image = \
+               env PATH="$(objtree)/scripts/dtc:$(PATH)" \
+               $(CONFIG_SHELL) $(MKIMAGE) \
+               -D "-I dts -O dtb -p 500 \
+                       --include $(objtree)/arch/mips \
+                       --warning no-unit_address_vs_reg" \
+               -f $(2) $@
+
+$(obj)/vmlinux.itb: $(obj)/vmlinux.its $(obj)/vmlinux.bin FORCE
+       $(call if_changed,itb-image,$<)
+
+$(obj)/vmlinux.gz.itb: $(obj)/vmlinux.gz.its $(obj)/vmlinux.bin.gz FORCE
+       $(call if_changed,itb-image,$<)
+
+$(obj)/vmlinux.bz2.itb: $(obj)/vmlinux.bz2.its $(obj)/vmlinux.bin.bz2 FORCE
+       $(call if_changed,itb-image,$<)
+
+$(obj)/vmlinux.lzma.itb: $(obj)/vmlinux.lzma.its $(obj)/vmlinux.bin.lzma FORCE
+       $(call if_changed,itb-image,$<)
+
+$(obj)/vmlinux.lzo.itb: $(obj)/vmlinux.lzo.its $(obj)/vmlinux.bin.lzo FORCE
+       $(call if_changed,itb-image,$<)
index fda9d387cc08640067ef93650aa01e78d4536007..d61bc2aebf69b423ba65fe1d02284fb2feed0814 100644 (file)
@@ -1,6 +1,5 @@
 dtb-$(CONFIG_DT_BCM93384WVG)           += bcm93384wvg.dtb
 dtb-$(CONFIG_DT_BCM93384WVG_VIPER)     += bcm93384wvg_viper.dtb
-dtb-$(CONFIG_DT_BCM96358NB4SER)                += bcm96358nb4ser.dtb
 dtb-$(CONFIG_DT_BCM96368MVWG)          += bcm96368mvwg.dtb
 dtb-$(CONFIG_DT_BCM9EJTAGPRB)          += bcm9ejtagprb.dtb
 dtb-$(CONFIG_DT_BCM97125CBMB)          += bcm97125cbmb.dtb
@@ -11,20 +10,29 @@ dtb-$(CONFIG_DT_BCM97362SVMB)               += bcm97362svmb.dtb
 dtb-$(CONFIG_DT_BCM97420C)             += bcm97420c.dtb
 dtb-$(CONFIG_DT_BCM97425SVMB)          += bcm97425svmb.dtb
 dtb-$(CONFIG_DT_BCM97435SVMB)          += bcm97435svmb.dtb
+dtb-$(CONFIG_DT_COMTREND_VR3032U)      += bcm63268-comtrend-vr-3032u.dtb
+dtb-$(CONFIG_DT_NETGEAR_CVG834G)       += bcm3368-netgear-cvg834g.dtb
+dtb-$(CONFIG_DT_SFR_NEUFBOX4_SERCOMM)  += bcm6358-neufbox4-sercomm.dtb
+dtb-$(CONFIG_DT_SFR_NEUFBOX6_SERCOMM)  += bcm6362-neufbox6-sercomm.dtb
 
-dtb-$(CONFIG_DT_NONE)                  += \
-                                               bcm93384wvg.dtb         \
-                                               bcm93384wvg_viper.dtb   \
-                                               bcm96358nb4ser.dtb      \
-                                               bcm96368mvwg.dtb        \
-                                               bcm9ejtagprb.dtb        \
-                                               bcm97125cbmb.dtb        \
-                                               bcm97346dbsmb.dtb       \
-                                               bcm97358svmb.dtb        \
-                                               bcm97360svmb.dtb        \
-                                               bcm97362svmb.dtb        \
-                                               bcm97420c.dtb           \
-                                               bcm97425svmb.dtb
+dtb-$(CONFIG_DT_NONE) += \
+       bcm3368-netgear-cvg834g.dtb \
+       bcm6358-neufbox4-sercomm.dtb \
+       bcm6362-neufbox6-sercomm.dtb \
+       bcm63268-comtrend-vr-3032u.dtb \
+       bcm93384wvg.dtb \
+       bcm93384wvg_viper.dtb \
+       bcm96358nb4ser.dtb \
+       bcm96368mvwg.dtb \
+       bcm9ejtagprb.dtb \
+       bcm97125cbmb.dtb \
+       bcm97346dbsmb.dtb \
+       bcm97358svmb.dtb \
+       bcm97360svmb.dtb \
+       bcm97362svmb.dtb \
+       bcm97420c.dtb \
+       bcm97425svmb.dtb \
+       bcm97435svmb.dtb
 
 obj-y                          += $(patsubst %.dtb, %.dtb.o, $(dtb-y))
 
diff --git a/arch/mips/boot/dts/brcm/bcm3368-netgear-cvg834g.dts b/arch/mips/boot/dts/brcm/bcm3368-netgear-cvg834g.dts
new file mode 100644 (file)
index 0000000..2f2e80f
--- /dev/null
@@ -0,0 +1,22 @@
+/dts-v1/;
+
+/include/ "bcm3368.dtsi"
+
+/ {
+       compatible = "netgear,cvg834g", "brcm,bcm3368";
+       model = "NETGEAR CVG834G";
+
+       memory@0 {
+               device_type = "memory";
+               reg = <0x00000000 0x02000000>;
+       };
+
+       chosen {
+               bootargs = "console=ttyS0,115200";
+               stdout-path = &uart0;
+       };
+};
+
+&uart0 {
+       status = "okay";
+};
diff --git a/arch/mips/boot/dts/brcm/bcm3368.dtsi b/arch/mips/boot/dts/brcm/bcm3368.dtsi
new file mode 100644 (file)
index 0000000..bee855c
--- /dev/null
@@ -0,0 +1,101 @@
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       compatible = "brcm,bcm3368";
+
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               mips-hpt-frequency = <150000000>;
+
+               cpu@0 {
+                       compatible = "brcm,bmips4350";
+                       device_type = "cpu";
+                       reg = <0>;
+               };
+
+               cpu@1 {
+                       compatible = "brcm,bmips4350";
+                       device_type = "cpu";
+                       reg = <1>;
+               };
+       };
+
+       clocks {
+               periph_clk: periph-clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+               };
+       };
+
+       aliases {
+               serial0 = &uart0;
+               serial1 = &uart1;
+       };
+
+       cpu_intc: interrupt-controller {
+               #address-cells = <0>;
+               compatible = "mti,cpu-interrupt-controller";
+
+               interrupt-controller;
+               #interrupt-cells = <1>;
+       };
+
+       ubus {
+               #address-cells = <1>;
+               #size-cells = <1>;
+
+               compatible = "simple-bus";
+               ranges;
+
+               periph_cntl: syscon@fff8c000 {
+                       compatible = "syscon";
+                       reg = <0xfff8c000 0xc>;
+                       native-endian;
+               };
+
+               reboot: syscon-reboot@fff8c008 {
+                       compatible = "syscon-reboot";
+                       regmap = <&periph_cntl>;
+                       offset = <0x8>;
+                       mask = <0x1>;
+               };
+
+               periph_intc: interrupt-controller@fff8c00c {
+                       compatible = "brcm,bcm6345-l1-intc";
+                       reg = <0xfff8c00c 0x8>;
+
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+
+                       interrupt-parent = <&cpu_intc>;
+                       interrupts = <2>;
+               };
+
+               uart0: serial@fff8c100 {
+                       compatible = "brcm,bcm6345-uart";
+                       reg = <0xfff8c100 0x18>;
+
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <2>;
+
+                       clocks = <&periph_clk>;
+
+                       status = "disabled";
+               };
+
+               uart1: serial@fff8c120 {
+                       compatible = "brcm,bcm6345-uart";
+                       reg = <0xfff8c120 0x18>;
+
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <3>;
+
+                       clocks = <&periph_clk>;
+
+                       status = "disabled";
+               };
+       };
+};
diff --git a/arch/mips/boot/dts/brcm/bcm63268-comtrend-vr-3032u.dts b/arch/mips/boot/dts/brcm/bcm63268-comtrend-vr-3032u.dts
new file mode 100644 (file)
index 0000000..430d35c
--- /dev/null
@@ -0,0 +1,108 @@
+/dts-v1/;
+
+/include/ "bcm63268.dtsi"
+
+/ {
+       compatible = "comtrend,vr-3032u", "brcm,bcm63268";
+       model = "Comtrend VR-3032u";
+
+       memory@0 {
+               device_type = "memory";
+               reg = <0x00000000 0x04000000>;
+       };
+
+       chosen {
+               bootargs = "console=ttyS0,115200";
+               stdout-path = &uart0;
+       };
+};
+
+&leds0 {
+       status = "ok";
+       brcm,serial-leds;
+       brcm,serial-dat-low;
+       brcm,serial-shift-inv;
+
+       led@0 {
+               reg = <0>;
+               brcm,hardware-controlled;
+               brcm,link-signal-sources = <0>;
+               /* GPHY0 Speed 0 */
+       };
+       led@1 {
+               reg = <1>;
+               brcm,hardware-controlled;
+               brcm,link-signal-sources = <1>;
+               /* GPHY0 Speed 1 */
+       };
+       led@2 {
+               reg = <2>;
+               active-low;
+               label = "vr-3032u:red:inet";
+       };
+       led@3 {
+               reg = <3>;
+               active-low;
+               label = "vr-3032u:green:dsl";
+       };
+       led@4 {
+               reg = <4>;
+               active-low;
+               label = "vr-3032u:green:usb";
+       };
+       led@7 {
+               reg = <7>;
+               active-low;
+               label = "vr-3032u:green:wps";
+       };
+       led@8 {
+               reg = <8>;
+               active-low;
+               label = "vr-3032u:green:inet";
+       };
+       led@9 {
+               reg = <9>;
+               brcm,hardware-controlled;
+               /* EPHY0 Activity */
+       };
+       led@10 {
+               reg = <10>;
+               brcm,hardware-controlled;
+               /* EPHY1 Activity */
+       };
+       led@11 {
+               reg = <11>;
+               brcm,hardware-controlled;
+               /* EPHY2 Activity */
+       };
+       led@12 {
+               reg = <12>;
+               brcm,hardware-controlled;
+               /* GPHY0 Activity */
+       };
+       led@13 {
+               reg = <13>;
+               brcm,hardware-controlled;
+               /* EPHY0 Speed */
+       };
+       led@14 {
+               reg = <14>;
+               brcm,hardware-controlled;
+               /* EPHY1 Speed */
+       };
+       led@15 {
+               reg = <15>;
+               brcm,hardware-controlled;
+               /* EPHY2 Speed */
+       };
+       led@20 {
+               reg = <20>;
+               active-low;
+               label = "vr-3032u:green:power";
+               default-state = "on";
+       };
+};
+
+&uart0 {
+       status = "okay";
+};
diff --git a/arch/mips/boot/dts/brcm/bcm63268.dtsi b/arch/mips/boot/dts/brcm/bcm63268.dtsi
new file mode 100644 (file)
index 0000000..7e6bf2c
--- /dev/null
@@ -0,0 +1,134 @@
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       compatible = "brcm,bcm63268";
+
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               mips-hpt-frequency = <200000000>;
+
+               cpu@0 {
+                       compatible = "brcm,bmips4350";
+                       device_type = "cpu";
+                       reg = <0>;
+               };
+
+               cpu@1 {
+                       compatible = "brcm,bmips4350";
+                       device_type = "cpu";
+                       reg = <1>;
+               };
+       };
+
+       clocks {
+               periph_clk: periph-clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+               };
+       };
+
+       aliases {
+               serial0 = &uart0;
+               serial1 = &uart1;
+       };
+
+       cpu_intc: interrupt-controller {
+               #address-cells = <0>;
+               compatible = "mti,cpu-interrupt-controller";
+
+               interrupt-controller;
+               #interrupt-cells = <1>;
+       };
+
+       ubus {
+               #address-cells = <1>;
+               #size-cells = <1>;
+
+               compatible = "simple-bus";
+               ranges;
+
+               periph_cntl: syscon@10000000 {
+                       compatible = "syscon";
+                       reg = <0x10000000 0x14>;
+                       native-endian;
+               };
+
+               reboot: syscon-reboot@10000008 {
+                       compatible = "syscon-reboot";
+                       regmap = <&periph_cntl>;
+                       offset = <0x8>;
+                       mask = <0x1>;
+               };
+
+               periph_intc: interrupt-controller@10000020 {
+                       compatible = "brcm,bcm6345-l1-intc";
+                       reg = <0x10000020 0x20>,
+                             <0x10000040 0x20>;
+
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+
+                       interrupt-parent = <&cpu_intc>;
+                       interrupts = <2>, <3>;
+               };
+
+               uart0: serial@10000180 {
+                       compatible = "brcm,bcm6345-uart";
+                       reg = <0x10000180 0x18>;
+
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <5>;
+
+                       clocks = <&periph_clk>;
+
+                       status = "disabled";
+               };
+
+               uart1: serial@100001a0 {
+                       compatible = "brcm,bcm6345-uart";
+                       reg = <0x100001a0 0x18>;
+
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <34>;
+
+                       clocks = <&periph_clk>;
+
+                       status = "disabled";
+               };
+
+               leds0: led-controller@10001900 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "brcm,bcm6328-leds";
+                       reg = <0x10001900 0x24>;
+
+                       status = "disabled";
+               };
+
+               ehci: usb@10002500 {
+                       compatible = "brcm,bcm63268-ehci", "generic-ehci";
+                       reg = <0x10002500 0x100>;
+                       big-endian;
+
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <10>;
+
+                       status = "disabled";
+               };
+
+               ohci: usb@10002600 {
+                       compatible = "brcm,bcm63268-ohci", "generic-ohci";
+                       reg = <0x10002600 0x100>;
+                       big-endian;
+                       no-big-frame-no;
+
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <9>;
+
+                       status = "disabled";
+               };
+       };
+};
similarity index 94%
rename from arch/mips/boot/dts/brcm/bcm96358nb4ser.dts
rename to arch/mips/boot/dts/brcm/bcm6358-neufbox4-sercomm.dts
index f412117972e6b65e32a8ff96978ebf2c6c2bdc8d..702eae2a22a0b2dd987a842e7fe2378bc19db5aa 100644 (file)
@@ -12,6 +12,7 @@
        };
 
        chosen {
+               bootargs = "console=ttyS0,115200";
                stdout-path = &uart0;
        };
 };
diff --git a/arch/mips/boot/dts/brcm/bcm6362-neufbox6-sercomm.dts b/arch/mips/boot/dts/brcm/bcm6362-neufbox6-sercomm.dts
new file mode 100644 (file)
index 0000000..480f2a5
--- /dev/null
@@ -0,0 +1,22 @@
+/dts-v1/;
+
+/include/ "bcm6362.dtsi"
+
+/ {
+       compatible = "sfr,nb6-ser", "brcm,bcm6362";
+       model = "SFR NeufBox 6 (Sercomm)";
+
+       memory@0 {
+               device_type = "memory";
+               reg = <0x00000000 0x08000000>;
+       };
+
+       chosen {
+               bootargs = "console=ttyS0,115200";
+               stdout-path = &uart0;
+       };
+};
+
+&uart0 {
+       status = "okay";
+};
diff --git a/arch/mips/boot/dts/brcm/bcm6362.dtsi b/arch/mips/boot/dts/brcm/bcm6362.dtsi
new file mode 100644 (file)
index 0000000..c507da5
--- /dev/null
@@ -0,0 +1,134 @@
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       compatible = "brcm,bcm6362";
+
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               mips-hpt-frequency = <200000000>;
+
+               cpu@0 {
+                       compatible = "brcm,bmips4350";
+                       device_type = "cpu";
+                       reg = <0>;
+               };
+
+               cpu@1 {
+                       compatible = "brcm,bmips4350";
+                       device_type = "cpu";
+                       reg = <1>;
+               };
+       };
+
+       clocks {
+               periph_clk: periph-clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+               };
+       };
+
+       aliases {
+               serial0 = &uart0;
+               serial1 = &uart1;
+       };
+
+       cpu_intc: interrupt-controller {
+               #address-cells = <0>;
+               compatible = "mti,cpu-interrupt-controller";
+
+               interrupt-controller;
+               #interrupt-cells = <1>;
+       };
+
+       ubus {
+               #address-cells = <1>;
+               #size-cells = <1>;
+
+               compatible = "simple-bus";
+               ranges;
+
+               periph_cntl: syscon@10000000 {
+                       compatible = "syscon";
+                       reg = <0x10000000 0x14>;
+                       native-endian;
+               };
+
+               reboot: syscon-reboot@10000008 {
+                       compatible = "syscon-reboot";
+                       regmap = <&periph_cntl>;
+                       offset = <0x8>;
+                       mask = <0x1>;
+               };
+
+               periph_intc: interrupt-controller@10000020 {
+                       compatible = "brcm,bcm6345-l1-intc";
+                       reg = <0x10000020 0x10>,
+                             <0x10000030 0x10>;
+
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+
+                       interrupt-parent = <&cpu_intc>;
+                       interrupts = <2>, <3>;
+               };
+
+               uart0: serial@10000100 {
+                       compatible = "brcm,bcm6345-uart";
+                       reg = <0x10000100 0x18>;
+
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <3>;
+
+                       clocks = <&periph_clk>;
+
+                       status = "disabled";
+               };
+
+               uart1: serial@10000120 {
+                       compatible = "brcm,bcm6345-uart";
+                       reg = <0x10000120 0x18>;
+
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <4>;
+
+                       clocks = <&periph_clk>;
+
+                       status = "disabled";
+               };
+
+               leds0: led-controller@10001900 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "brcm,bcm6328-leds";
+                       reg = <0x10001900 0x24>;
+
+                       status = "disabled";
+               };
+
+               ehci: usb@10002500 {
+                       compatible = "brcm,bcm6362-ehci", "generic-ehci";
+                       reg = <0x10002500 0x100>;
+                       big-endian;
+
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <10>;
+
+                       status = "disabled";
+               };
+
+               ohci: usb@10002600 {
+                       compatible = "brcm,bcm6362-ohci", "generic-ohci";
+                       reg = <0x10002600 0x100>;
+                       big-endian;
+                       no-big-frame-no;
+
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <9>;
+
+                       status = "disabled";
+               };
+       };
+};
index 550e1d9e3ee039eb06f14816bde573a03268ef4c..bbd00f65ce397a7a83b762f7dc5da316efa427fb 100644 (file)
@@ -26,7 +26,7 @@
                uart0 = &uart0;
        };
 
-       cpu_intc: cpu_intc {
+       cpu_intc: interrupt-controller {
                #address-cells = <0>;
                compatible = "mti,cpu-interrupt-controller";
 
                        #clock-cells = <0>;
                        clock-frequency = <81000000>;
                };
+
+               upg_clk: upg_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <27000000>;
+               };
        };
 
        rdb {
@@ -49,7 +55,7 @@
                compatible = "simple-bus";
                ranges = <0 0x10000000 0x01000000>;
 
-               periph_intc: periph_intc@441400 {
+               periph_intc: interrupt-controller@441400 {
                        compatible = "brcm,bcm7038-l1-intc";
                        reg = <0x441400 0x30>, <0x441600 0x30>;
 
@@ -60,7 +66,7 @@
                        interrupts = <2>, <3>;
                };
 
-               sun_l2_intc: sun_l2_intc@401800 {
+               sun_l2_intc: interrupt-controller@401800 {
                        compatible = "brcm,l2-intc";
                        reg = <0x401800 0x30>;
                        interrupt-controller;
@@ -81,7 +87,7 @@
                                                     "avd_0", "jtag_0";
                };
 
-               upg_irq0_intc: upg_irq0_intc@406780 {
+               upg_irq0_intc: interrupt-controller@406780 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x406780 0x8>;
 
                      status = "disabled";
                };
 
+               pwma: pwm@406580 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406580 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               upg_gio: gpio@406700 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x406700 0x80>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_irq0_intc>;
+                       interrupts = <6>;
+                       brcm,gpio-bank-widths = <32 32 32 18>;
+               };
+
                ehci0: usb@488300 {
                        compatible = "brcm,bcm7125-ehci", "generic-ehci";
                        reg = <0x488300 0x100>;
index ec959061d52e30ef96c71fd78bd07f72b63c124e..4bbcc95f1c15d6dee9f2124d4318d60fba246b99 100644 (file)
@@ -26,7 +26,7 @@
                uart0 = &uart0;
        };
 
-       cpu_intc: cpu_intc {
+       cpu_intc: interrupt-controller {
                #address-cells = <0>;
                compatible = "mti,cpu-interrupt-controller";
 
                        #clock-cells = <0>;
                        clock-frequency = <81000000>;
                };
+
+               upg_clk: upg_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <27000000>;
+               };
        };
 
        rdb {
@@ -49,7 +55,7 @@
                compatible = "simple-bus";
                ranges = <0 0x10000000 0x01000000>;
 
-               periph_intc: periph_intc@411400 {
+               periph_intc: interrupt-controller@411400 {
                        compatible = "brcm,bcm7038-l1-intc";
                        reg = <0x411400 0x30>, <0x411600 0x30>;
 
@@ -60,7 +66,7 @@
                        interrupts = <2>, <3>;
                };
 
-               sun_l2_intc: sun_l2_intc@403000 {
+               sun_l2_intc: interrupt-controller@403000 {
                        compatible = "brcm,l2-intc";
                        reg = <0x403000 0x30>;
                        interrupt-controller;
@@ -81,7 +87,7 @@
                                                     "jtag_0", "svd_0";
                };
 
-               upg_irq0_intc: upg_irq0_intc@406780 {
+               upg_irq0_intc: interrupt-controller@406780 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x406780 0x8>;
 
                        interrupt-names = "upg_main", "upg_bsc";
                };
 
-               upg_aon_irq0_intc: upg_aon_irq0_intc@408b80 {
+               upg_aon_irq0_intc: interrupt-controller@408b80 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x408b80 0x8>;
 
                      status = "disabled";
                };
 
+               pwma: pwm@406580 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406580 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               pwmb: pwm@406800 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406800 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               aon_pm_l2_intc: interrupt-controller@408440 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x408440 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <53>;
+                       brcm,irq-can-wake;
+               };
+
+               upg_gio: gpio@406700 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x406700 0x60>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_irq0_intc>;
+                       interrupts = <6>;
+                       brcm,gpio-bank-widths = <32 32 16>;
+               };
+
+               upg_gio_aon: gpio@408c00 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x408c00 0x60>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_aon_irq0_intc>;
+                       interrupts = <6>;
+                       interrupts-extended = <&upg_aon_irq0_intc 6>,
+                                             <&aon_pm_l2_intc 5>;
+                       wakeup-source;
+                       brcm,gpio-bank-widths = <27 32 2>;
+               };
+
                enet0: ethernet@430000 {
                        phy-mode = "internal";
                        phy-handle = <&phy1>;
                        status = "disabled";
                };
 
+               hif_l2_intc: interrupt-controller@411000 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x411000 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <30>;
+               };
+
+               nand: nand@412800 {
+                       compatible = "brcm,brcmnand-v5.0", "brcm,brcmnand";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg-names = "nand";
+                       reg = <0x412800 0x400>;
+                       interrupt-parent = <&hif_l2_intc>;
+                       interrupts = <24>;
+                       status = "disabled";
+               };
+
                sata: sata@181000 {
                        compatible = "brcm,bcm7425-ahci", "brcm,sata3-ahci";
                        reg-names = "ahci", "top-ctrl";
                                #phy-cells = <0>;
                        };
                };
+
+               sdhci0: sdhci@413500 {
+                       compatible = "brcm,bcm7425-sdhci";
+                       reg = <0x413500 0x100>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <85>;
+                       status = "disabled";
+               };
        };
 };
index ca57fb5eb1222e4e42066e4b99d5239a85121791..3e42535c8d290907705172bcdb86387983564c34 100644 (file)
@@ -20,7 +20,7 @@
                uart0 = &uart0;
        };
 
-       cpu_intc: cpu_intc {
+       cpu_intc: interrupt-controller {
                #address-cells = <0>;
                compatible = "mti,cpu-interrupt-controller";
 
                        #clock-cells = <0>;
                        clock-frequency = <81000000>;
                };
+
+               upg_clk: upg_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <27000000>;
+               };
        };
 
        rdb {
@@ -43,7 +49,7 @@
                compatible = "simple-bus";
                ranges = <0 0x10000000 0x01000000>;
 
-               periph_intc: periph_intc@411400 {
+               periph_intc: interrupt-controller@411400 {
                        compatible = "brcm,bcm7038-l1-intc";
                        reg = <0x411400 0x30>;
 
@@ -54,7 +60,7 @@
                        interrupts = <2>;
                };
 
-               sun_l2_intc: sun_l2_intc@403000 {
+               sun_l2_intc: interrupt-controller@403000 {
                        compatible = "brcm,l2-intc";
                        reg = <0x403000 0x30>;
                        interrupt-controller;
@@ -75,7 +81,7 @@
                                                     "avd_0", "jtag_0";
                };
 
-               upg_irq0_intc: upg_irq0_intc@406600 {
+               upg_irq0_intc: interrupt-controller@406600 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x406600 0x8>;
 
@@ -90,7 +96,7 @@
                        interrupt-names = "upg_main", "upg_bsc";
                };
 
-               upg_aon_irq0_intc: upg_aon_irq0_intc@408b80 {
+               upg_aon_irq0_intc: interrupt-controller@408b80 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x408b80 0x8>;
 
                      status = "disabled";
                };
 
+               pwma: pwm@406400 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406400 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               pwmb: pwm@406700 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406700 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               aon_pm_l2_intc: interrupt-controller@408240 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x408240 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <50>;
+                       brcm,irq-can-wake;
+               };
+
+               upg_gio: gpio@406500 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x406500 0xa0>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_irq0_intc>;
+                       interrupts = <6>;
+                       brcm,gpio-bank-widths = <32 32 32 29 4>;
+               };
+
+               upg_gio_aon: gpio@408c00 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x408c00 0x60>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_aon_irq0_intc>;
+                       interrupts = <6>;
+                       interrupts-extended = <&upg_aon_irq0_intc 6>,
+                                             <&aon_pm_l2_intc 5>;
+                       wakeup-source;
+                       brcm,gpio-bank-widths = <21 32 2>;
+               };
+
                enet0: ethernet@430000 {
                        phy-mode = "internal";
                        phy-handle = <&phy1>;
                        interrupts = <66>;
                        status = "disabled";
                };
+
+               hif_l2_intc: interrupt-controller@411000 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x411000 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <30>;
+               };
+
+               nand: nand@412800 {
+                       compatible = "brcm,brcmnand-v5.0", "brcm,brcmnand";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg-names = "nand";
+                       reg = <0x412800 0x400>;
+                       interrupt-parent = <&hif_l2_intc>;
+                       interrupts = <24>;
+                       status = "disabled";
+               };
        };
 };
index 1c0c3d438c7ac42b6df46523077c8804bdaf99b8..112a5571c5961c2b5939a81881967121515bf802 100644 (file)
@@ -20,7 +20,7 @@
                uart0 = &uart0;
        };
 
-       cpu_intc: cpu_intc {
+       cpu_intc: interrupt-controller {
                #address-cells = <0>;
                compatible = "mti,cpu-interrupt-controller";
 
                        #clock-cells = <0>;
                        clock-frequency = <81000000>;
                };
+
+               upg_clk: upg_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <27000000>;
+               };
        };
 
        rdb {
@@ -43,7 +49,7 @@
                compatible = "simple-bus";
                ranges = <0 0x10000000 0x01000000>;
 
-               periph_intc: periph_intc@411400 {
+               periph_intc: interrupt-controller@411400 {
                        compatible = "brcm,bcm7038-l1-intc";
                        reg = <0x411400 0x30>;
 
@@ -54,7 +60,7 @@
                        interrupts = <2>;
                };
 
-               sun_l2_intc: sun_l2_intc@403000 {
+               sun_l2_intc: interrupt-controller@403000 {
                        compatible = "brcm,l2-intc";
                        reg = <0x403000 0x30>;
                        interrupt-controller;
@@ -75,7 +81,7 @@
                                                     "avd_0", "jtag_0";
                };
 
-               upg_irq0_intc: upg_irq0_intc@406600 {
+               upg_irq0_intc: interrupt-controller@406600 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x406600 0x8>;
 
@@ -90,7 +96,7 @@
                        interrupt-names = "upg_main", "upg_bsc";
                };
 
-               upg_aon_irq0_intc: upg_aon_irq0_intc@408b80 {
+               upg_aon_irq0_intc: interrupt-controller@408b80 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x408b80 0x8>;
 
                      status = "disabled";
                };
 
+               pwma: pwm@406400 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406400 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               aon_pm_l2_intc: interrupt-controller@408440 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x408440 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <50>;
+                       brcm,irq-can-wake;
+               };
+
+               upg_gio: gpio@406500 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x406500 0xa0>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_irq0_intc>;
+                       interrupts = <6>;
+                       brcm,gpio-bank-widths = <32 32 32 29 4>;
+               };
+
+               upg_gio_aon: gpio@408c00 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x408c00 0x60>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_aon_irq0_intc>;
+                       interrupts = <6>;
+                       interrupts-extended = <&upg_aon_irq0_intc 6>,
+                                             <&aon_pm_l2_intc 5>;
+                       wakeup-source;
+                       brcm,gpio-bank-widths = <21 32 2>;
+               };
+
                enet0: ethernet@430000 {
                        phy-mode = "internal";
                        phy-handle = <&phy1>;
                        status = "disabled";
                };
 
+               hif_l2_intc: interrupt-controller@411000 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x411000 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <30>;
+               };
+
+               nand: nand@412800 {
+                       compatible = "brcm,brcmnand-v5.0", "brcm,brcmnand";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg-names = "nand";
+                       reg = <0x412800 0x400>;
+                       interrupt-parent = <&hif_l2_intc>;
+                       interrupts = <24>;
+                       status = "disabled";
+               };
+
                sata: sata@181000 {
                        compatible = "brcm,bcm7425-ahci", "brcm,sata3-ahci";
                        reg-names = "ahci", "top-ctrl";
                                #phy-cells = <0>;
                        };
                };
+
+               sdhci0: sdhci@410000 {
+                       compatible = "brcm,bcm7425-sdhci";
+                       reg = <0x410000 0x100>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <82>;
+                       status = "disabled";
+               };
        };
 };
index 6b4713add4b8a9e176d7556fe2a27360cb7f745d..34abfb0b07e79406e23bd2bd63396aea282992a5 100644 (file)
@@ -26,7 +26,7 @@
                uart0 = &uart0;
        };
 
-       cpu_intc: cpu_intc {
+       cpu_intc: interrupt-controller {
                #address-cells = <0>;
                compatible = "mti,cpu-interrupt-controller";
 
                        #clock-cells = <0>;
                        clock-frequency = <81000000>;
                };
+
+               upg_clk: upg_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <27000000>;
+               };
        };
 
        rdb {
@@ -49,7 +55,7 @@
                compatible = "simple-bus";
                ranges = <0 0x10000000 0x01000000>;
 
-               periph_intc: periph_intc@411400 {
+               periph_intc: interrupt-controller@411400 {
                        compatible = "brcm,bcm7038-l1-intc";
                        reg = <0x411400 0x30>, <0x411600 0x30>;
 
@@ -60,7 +66,7 @@
                        interrupts = <2>, <3>;
                };
 
-               sun_l2_intc: sun_l2_intc@403000 {
+               sun_l2_intc: interrupt-controller@403000 {
                        compatible = "brcm,l2-intc";
                        reg = <0x403000 0x30>;
                        interrupt-controller;
@@ -81,7 +87,7 @@
                                                     "avd_0", "jtag_0";
                };
 
-               upg_irq0_intc: upg_irq0_intc@406600 {
+               upg_irq0_intc: interrupt-controller@406600 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x406600 0x8>;
 
                        interrupt-names = "upg_main", "upg_bsc";
                };
 
-               upg_aon_irq0_intc: upg_aon_irq0_intc@408b80 {
+               upg_aon_irq0_intc: interrupt-controller@408b80 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x408b80 0x8>;
 
                      status = "disabled";
                };
 
+               pwma: pwm@406400 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406400 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               aon_pm_l2_intc: interrupt-controller@408440 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x408440 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <50>;
+                       brcm,irq-can-wake;
+               };
+
+               upg_gio: gpio@406500 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x406500 0xa0>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_irq0_intc>;
+                       interrupts = <6>;
+                       brcm,gpio-bank-widths = <32 32 32 29 4>;
+               };
+
+               upg_gio_aon: gpio@408c00 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x408c00 0x60>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_aon_irq0_intc>;
+                       interrupts = <6>;
+                       interrupts-extended = <&upg_aon_irq0_intc 6>,
+                                             <&aon_pm_l2_intc 5>;
+                       wakeup-source;
+                       brcm,gpio-bank-widths = <21 32 2>;
+               };
+
                enet0: ethernet@430000 {
                        phy-mode = "internal";
                        phy-handle = <&phy1>;
                        status = "disabled";
                };
 
+               hif_l2_intc: interrupt-controller@411000 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x411000 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <30>;
+               };
+
+               nand: nand@412800 {
+                       compatible = "brcm,brcmnand-v5.0", "brcm,brcmnand";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg-names = "nand";
+                       reg = <0x412800 0x400>;
+                       interrupt-parent = <&hif_l2_intc>;
+                       interrupts = <24>;
+                       status = "disabled";
+               };
+
                sata: sata@181000 {
                        compatible = "brcm,bcm7425-ahci", "brcm,sata3-ahci";
                        reg-names = "ahci", "top-ctrl";
                                #phy-cells = <0>;
                        };
                };
+
+               sdhci0: sdhci@410000 {
+                       compatible = "brcm,bcm7425-sdhci";
+                       reg = <0x410000 0x100>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <82>;
+                       status = "disabled";
+               };
        };
 };
index 0586bf662571e633609517c573e147dddc584c6f..b143723c674e8d4b15f50940175d4e87f5b0cc47 100644 (file)
@@ -26,7 +26,7 @@
                uart0 = &uart0;
        };
 
-       cpu_intc: cpu_intc {
+       cpu_intc: interrupt-controller {
                #address-cells = <0>;
                compatible = "mti,cpu-interrupt-controller";
 
                        #clock-cells = <0>;
                        clock-frequency = <81000000>;
                };
+
+               upg_clk: upg_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <27000000>;
+               };
        };
 
        rdb {
@@ -49,7 +55,7 @@
                compatible = "simple-bus";
                ranges = <0 0x10000000 0x01000000>;
 
-               periph_intc: periph_intc@441400 {
+               periph_intc: interrupt-controller@441400 {
                        compatible = "brcm,bcm7038-l1-intc";
                        reg = <0x441400 0x30>, <0x441600 0x30>;
 
@@ -60,7 +66,7 @@
                        interrupts = <2>, <3>;
                };
 
-               sun_l2_intc: sun_l2_intc@401800 {
+               sun_l2_intc: interrupt-controller@401800 {
                        compatible = "brcm,l2-intc";
                        reg = <0x401800 0x30>;
                        interrupt-controller;
@@ -82,7 +88,7 @@
                                                     "jtag_0";
                };
 
-               upg_irq0_intc: upg_irq0_intc@406780 {
+               upg_irq0_intc: interrupt-controller@406780 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x406780 0x8>;
 
                      status = "disabled";
                };
 
+               pwma: pwm@406580 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406580 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               pwmb: pwm@406880 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406880 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               upg_gio: gpio@406700 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x406700 0x80>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_irq0_intc>;
+                       interrupts = <6>;
+                       brcm,gpio-bank-widths = <32 32 32 27>;
+               };
+
                enet0: ethernet@468000 {
                        phy-mode = "internal";
                        phy-handle = <&phy1>;
index c1c15edaf829ba231eeac9afa77c42876e530593..2488d2f61f6017a26f0d1d9198421e5ee6ae35a4 100644 (file)
@@ -26,7 +26,7 @@
                uart0 = &uart0;
        };
 
-       cpu_intc: cpu_intc {
+       cpu_intc: interrupt-controller {
                #address-cells = <0>;
                compatible = "mti,cpu-interrupt-controller";
 
                        #clock-cells = <0>;
                        clock-frequency = <81000000>;
                };
+
+               upg_clk: upg_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <27000000>;
+               };
        };
 
        rdb {
@@ -49,7 +55,7 @@
                compatible = "simple-bus";
                ranges = <0 0x10000000 0x01000000>;
 
-               periph_intc: periph_intc@41a400 {
+               periph_intc: interrupt-controller@41a400 {
                        compatible = "brcm,bcm7038-l1-intc";
                        reg = <0x41a400 0x30>, <0x41a600 0x30>;
 
@@ -60,7 +66,7 @@
                        interrupts = <2>, <3>;
                };
 
-               sun_l2_intc: sun_l2_intc@403000 {
+               sun_l2_intc: interrupt-controller@403000 {
                        compatible = "brcm,l2-intc";
                        reg = <0x403000 0x30>;
                        interrupt-controller;
@@ -83,7 +89,7 @@
                                                     "vice_0";
                };
 
-               upg_irq0_intc: upg_irq0_intc@406780 {
+               upg_irq0_intc: interrupt-controller@406780 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x406780 0x8>;
 
                        interrupt-names = "upg_main", "upg_bsc";
                };
 
-               upg_aon_irq0_intc: upg_aon_irq0_intc@409480 {
+               upg_aon_irq0_intc: interrupt-controller@409480 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x409480 0x8>;
 
                      status = "disabled";
                };
 
+               pwma: pwm@406580 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406580 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               pwmb: pwm@406800 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406800 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               aon_pm_l2_intc: interrupt-controller@408440 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x408440 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <49>;
+                       brcm,irq-can-wake;
+               };
+
+               upg_gio: gpio@406700 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x406700 0x80>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_irq0_intc>;
+                       interrupts = <6>;
+                       brcm,gpio-bank-widths = <32 32 32 21>;
+               };
+
+               upg_gio_aon: gpio@4094c0 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x4094c0 0x40>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_aon_irq0_intc>;
+                       interrupts = <6>;
+                       interrupts-extended = <&upg_aon_irq0_intc 6>,
+                                             <&aon_pm_l2_intc 5>;
+                       wakeup-source;
+                       brcm,gpio-bank-widths = <18 4>;
+               };
+
                enet0: ethernet@b80000 {
                        phy-mode = "internal";
                        phy-handle = <&phy1>;
                        status = "disabled";
                };
 
+               hif_l2_intc: interrupt-controller@41a000 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x41a000 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <24>;
+               };
+
+               nand: nand@41b800 {
+                       compatible = "brcm,brcmnand-v5.0", "brcm,brcmnand";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg-names = "nand";
+                       reg = <0x41b800 0x400>;
+                       interrupt-parent = <&hif_l2_intc>;
+                       interrupts = <24>;
+                       status = "disabled";
+               };
+
                sata: sata@181000 {
                        compatible = "brcm,bcm7425-ahci", "brcm,sata3-ahci";
                        reg-names = "ahci", "top-ctrl";
                                #phy-cells = <0>;
                        };
                };
+
+               sdhci0: sdhci@419000 {
+                       compatible = "brcm,bcm7425-sdhci";
+                       reg = <0x419000 0x100>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <43>;
+                       sd-uhs-sdr50;
+                       mmc-hs200-1_8v;
+                       status = "disabled";
+               };
+
+               sdhci1: sdhci@419200 {
+                       compatible = "brcm,bcm7425-sdhci";
+                       reg = <0x419200 0x100>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <44>;
+                       sd-uhs-sdr50;
+                       mmc-hs200-1_8v;
+                       status = "disabled";
+               };
        };
 };
index a874d3a0e2ee637402e3feef5c5a45a6fca1732f..19fa259b968b3fc7b1ab476a4ed27125b6af8862 100644 (file)
@@ -38,7 +38,7 @@
                uart0 = &uart0;
        };
 
-       cpu_intc: cpu_intc {
+       cpu_intc: interrupt-controller {
                #address-cells = <0>;
                compatible = "mti,cpu-interrupt-controller";
 
                        #clock-cells = <0>;
                        clock-frequency = <81000000>;
                };
+
+               upg_clk: upg_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <27000000>;
+               };
        };
 
        rdb {
@@ -61,7 +67,7 @@
                compatible = "simple-bus";
                ranges = <0 0x10000000 0x01000000>;
 
-               periph_intc: periph_intc@41b500 {
+               periph_intc: interrupt-controller@41b500 {
                        compatible = "brcm,bcm7038-l1-intc";
                        reg = <0x41b500 0x40>, <0x41b600 0x40>,
                                <0x41b700 0x40>, <0x41b800 0x40>;
@@ -73,7 +79,7 @@
                        interrupts = <2>, <3>, <2>, <3>;
                };
 
-               sun_l2_intc: sun_l2_intc@403000 {
+               sun_l2_intc: interrupt-controller@403000 {
                        compatible = "brcm,l2-intc";
                        reg = <0x403000 0x30>;
                        interrupt-controller;
                                                     "scpu";
                };
 
-               upg_irq0_intc: upg_irq0_intc@406780 {
+               upg_irq0_intc: interrupt-controller@406780 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x406780 0x8>;
 
                        interrupt-names = "upg_main", "upg_bsc";
                };
 
-               upg_aon_irq0_intc: upg_aon_irq0_intc@409480 {
+               upg_aon_irq0_intc: interrupt-controller@409480 {
                        compatible = "brcm,bcm7120-l2-intc";
                        reg = <0x409480 0x8>;
 
                      status = "disabled";
                };
 
+               pwma: pwm@406580 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406580 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               pwmb: pwm@406800 {
+                       compatible = "brcm,bcm7038-pwm";
+                       reg = <0x406800 0x28>;
+                       #pwm-cells = <2>;
+                       clocks = <&upg_clk>;
+                       status = "disabled";
+               };
+
+               aon_pm_l2_intc: interrupt-controller@408440 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x408440 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <54>;
+                       brcm,irq-can-wake;
+               };
+
+               upg_gio: gpio@406700 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x406700 0x80>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_irq0_intc>;
+                       interrupts = <6>;
+                       brcm,gpio-bank-widths = <32 32 32 21>;
+               };
+
+               upg_gio_aon: gpio@4094c0 {
+                       compatible = "brcm,brcmstb-gpio";
+                       reg = <0x4094c0 0x40>;
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       interrupt-parent = <&upg_aon_irq0_intc>;
+                       interrupts = <6>;
+                       interrupts-extended = <&upg_aon_irq0_intc 6>,
+                                             <&aon_pm_l2_intc 5>;
+                       wakeup-source;
+                       brcm,gpio-bank-widths = <18 4>;
+               };
+
                enet0: ethernet@b80000 {
                        phy-mode = "internal";
                        phy-handle = <&phy1>;
                        status = "disabled";
                };
 
+               hif_l2_intc: interrupt-controller@41b000 {
+                       compatible = "brcm,l2-intc";
+                       reg = <0x41b000 0x30>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <24>;
+               };
+
+               nand: nand@41c800 {
+                       compatible = "brcm,brcmnand-v6.2", "brcm,brcmnand";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg-names = "nand", "flash-dma";
+                       reg = <0x41c800 0x600>, <0x41d000 0x100>;
+                       interrupt-parent = <&hif_l2_intc>;
+                       interrupts = <24>, <4>;
+                       status = "disabled";
+               };
+
                sata: sata@181000 {
                        compatible = "brcm,bcm7425-ahci", "brcm,sata3-ahci";
                        reg-names = "ahci", "top-ctrl";
                                #phy-cells = <0>;
                        };
                };
+
+               sdhci0: sdhci@41a000 {
+                       compatible = "brcm,bcm7425-sdhci";
+                       reg = <0x41a000 0x100>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <47>;
+                       sd-uhs-sdr50;
+                       mmc-hs200-1_8v;
+                       status = "disabled";
+               };
+
+               sdhci1: sdhci@41a200 {
+                       compatible = "brcm,bcm7425-sdhci";
+                       reg = <0x41a200 0x100>;
+                       interrupt-parent = <&periph_intc>;
+                       interrupts = <48>;
+                       sd-uhs-sdr50;
+                       mmc-hs200-1_8v;
+                       status = "disabled";
+               };
        };
 };
index f2449d147c6da9177010d869f90ac0d1ead9e468..5c24eacd72ddce0a5b354275664f528896af6e83 100644 (file)
        status = "okay";
 };
 
+&pwma {
+       status = "okay";
+};
+
 /* FIXME: USB is wonky; disable it for now */
 &ehci0 {
        status = "disabled";
index d3d28816a0270716d9634b553bcc30962584ef1a..e67eaf30de3d131ac4432ed0f8a7097a6a8d89a6 100644 (file)
@@ -1,6 +1,7 @@
 /dts-v1/;
 
 /include/ "bcm7346.dtsi"
+/include/ "bcm97xxx-nand-cs1-bch24.dtsi"
 
 / {
        compatible = "brcm,bcm97346dbsmb", "brcm,bcm7346";
        status = "okay";
 };
 
+&pwma {
+       status = "okay";
+};
+
+&pwmb {
+       status = "okay";
+};
+
 &enet0 {
        status = "okay";
 };
        status = "okay";
 };
 
+&nand {
+       status = "okay";
+};
+
 &sata {
        status = "okay";
 };
 &sata_phy {
        status = "okay";
 };
+
+&sdhci0 {
+       status = "okay";
+};
index 02ce6b429dc47b104633491cee178499e677f1f4..ee4607fae47accb047197ded65b43e09aea7addb 100644 (file)
@@ -1,6 +1,7 @@
 /dts-v1/;
 
 /include/ "bcm7358.dtsi"
+/include/ "bcm97xxx-nand-cs1-bch4.dtsi"
 
 / {
        compatible = "brcm,bcm97358svmb", "brcm,bcm7358";
        status = "okay";
 };
 
+&pwma {
+       status = "okay";
+};
+
+&pwmb {
+       status = "okay";
+};
+
 &enet0 {
        status = "okay";
 };
@@ -56,3 +65,7 @@
 &ohci0 {
        status = "okay";
 };
+
+&nand {
+       status = "okay";
+};
index 73124be9548aeff58f1fd507883c3f2637808a55..bed821b030139599e7fddb0f0de2c38d77fffb7d 100644 (file)
        status = "okay";
 };
 
+&pwma {
+       status = "okay";
+};
+
 &enet0 {
        status = "okay";
 };
@@ -64,3 +68,7 @@
 &sata_phy {
        status = "okay";
 };
+
+&sdhci0 {
+       status = "okay";
+};
index 3cfcaebe7f79db34e8c340ba71a1238f327b78c3..68fd823868e07a3f580ca97960a93f78d36afaae 100644 (file)
@@ -1,6 +1,7 @@
 /dts-v1/;
 
 /include/ "bcm7362.dtsi"
+/include/ "bcm97xxx-nand-cs1-bch4.dtsi"
 
 / {
        compatible = "brcm,bcm97362svmb", "brcm,bcm7362";
        status = "okay";
 };
 
+&pwma {
+       status = "okay";
+};
+
 &enet0 {
        status = "okay";
 };
        status = "okay";
 };
 
+&nand {
+       status = "okay";
+};
+
 &sata {
        status = "okay";
 };
@@ -60,3 +69,7 @@
 &sata_phy {
        status = "okay";
 };
+
+&sdhci0 {
+       status = "okay";
+};
index 600d57abee05c1bb8e8f22c57d67f1fb7a509a8c..e66271af055e74fd19c41f8390019c19c2fd08b9 100644 (file)
        status = "okay";
 };
 
+&pwma {
+       status = "okay";
+};
+
+&pwmb {
+       status = "okay";
+};
+
 /* FIXME: MAC driver comes up but cannot attach to PHY */
 &enet0 {
        status = "disabled";
index 119c714805cbc8257f1addc89a9b17289ffa73ec..f95ba1bf3e5806d0a4b4467621b16eb2490cc2ed 100644 (file)
@@ -1,6 +1,7 @@
 /dts-v1/;
 
 /include/ "bcm7425.dtsi"
+/include/ "bcm97xxx-nand-cs1-bch24.dtsi"
 
 / {
        compatible = "brcm,bcm97425svmb", "brcm,bcm7425";
        status = "okay";
 };
 
+&pwma {
+       status = "okay";
+};
+
+&pwmb {
+       status = "okay";
+};
+
 &enet0 {
        status = "okay";
 };
 &ohci3 {
        status = "okay";
 };
+
+&nand {
+       status = "okay";
+};
+
+&sdhci0 {
+       status = "okay";
+};
+
+&sdhci1 {
+       status = "okay";
+};
index 43e3ba27f07ba2ddc6935a7f80ba4a2b238f67ca..fb37b7111bf4f39bbcac24af96a9f0fdf4dd9a02 100644 (file)
@@ -1,6 +1,7 @@
 /dts-v1/;
 
 /include/ "bcm7435.dtsi"
+/include/ "bcm97xxx-nand-cs1-bch24.dtsi"
 
 / {
        compatible = "brcm,bcm97435svmb", "brcm,bcm7435";
        status = "okay";
 };
 
+&pwma {
+       status = "okay";
+};
+
+&pwmb {
+       status = "okay";
+};
+
 &enet0 {
        status = "okay";
 };
        status = "okay";
 };
 
+&nand {
+       status = "okay";
+};
+
 &sata {
        status = "okay";
 };
 &sata_phy {
        status = "okay";
 };
+
+&sdhci0 {
+       status = "okay";
+};
+
+&sdhci1 {
+       status = "okay";
+};
diff --git a/arch/mips/boot/dts/brcm/bcm97xxx-nand-cs1-bch24.dtsi b/arch/mips/boot/dts/brcm/bcm97xxx-nand-cs1-bch24.dtsi
new file mode 100644 (file)
index 0000000..3c24f97
--- /dev/null
@@ -0,0 +1,25 @@
+&nand {
+       nandcs@1 {
+               compatible = "brcm,nandcs";
+               reg = <1>;
+               nand-on-flash-bbt;
+
+               nand-ecc-strength = <24>;
+               nand-ecc-step-size = <1024>;
+               brcm,nand-oob-sector-size = <27>;
+
+               partitions {
+                       compatible = "fixed-partitions";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       flash1.rootfs@0 {
+                               reg = <0x0 0x10000000>;
+                       };
+
+                       flash1.kernel@10000000 {
+                               reg = <0x10000000 0x400000>;
+                       };
+               };
+       };
+};
diff --git a/arch/mips/boot/dts/brcm/bcm97xxx-nand-cs1-bch4.dtsi b/arch/mips/boot/dts/brcm/bcm97xxx-nand-cs1-bch4.dtsi
new file mode 100644 (file)
index 0000000..cb53181
--- /dev/null
@@ -0,0 +1,25 @@
+&nand {
+       nandcs@1 {
+               compatible = "brcm,nandcs";
+               reg = <1>;
+               nand-on-flash-bbt;
+
+               nand-ecc-strength = <4>;
+               nand-ecc-step-size = <512>;
+               brcm,nand-oob-sector-size = <16>;
+
+               partitions {
+                       compatible = "fixed-partitions";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       flash1.rootfs@0 {
+                               reg = <0x0 0x10000000>;
+                       };
+
+                       flash1.kernel@10000000 {
+                               reg = <0x10000000 0x400000>;
+                       };
+               };
+       };
+};
index b134798a0fd7252798620add0cd15c705cbea37d..cfa29156eb69df0775af66316f0ffcdfa4e7513d 100644 (file)
@@ -8,55 +8,16 @@
  * published by the Free Software Foundation.
  */
 
-/include/ "octeon_3xxx.dtsi"
+/include/ "dlink_dsr-500n-1000n.dtsi"
 #include <dt-bindings/gpio/gpio.h>
 
 / {
        model = "dlink,dsr-1000n";
 
        soc@0 {
-               smi0: mdio@1180000001800 {
-                       phy8: ethernet-phy@8 {
-                               reg = <8>;
-                               compatible = "ethernet-phy-ieee802.3-c22";
-                       };
-               };
-
-               pip: pip@11800a0000000 {
-                       interface@0 {
-                               ethernet@0 {
-                                       fixed-link {
-                                               speed = <1000>;
-                                               full-duplex;
-                                       };
-                               };
-                               ethernet@1 {
-                                       fixed-link {
-                                               speed = <1000>;
-                                               full-duplex;
-                                       };
-                               };
-                               ethernet@2 {
-                                       phy-handle = <&phy8>;
-                               };
-                       };
-               };
-
-               twsi0: i2c@1180000001000 {
-                       rtc@68 {
-                               compatible = "dallas,ds1337";
-                               reg = <0x68>;
-                       };
-               };
-
                uart0: serial@1180000000800 {
                        clock-frequency = <500000000>;
                };
-
-               usbn: usbn@1180068000000 {
-                       refclk-frequency = <12000000>;
-                       refclk-type = "crystal";
-               };
        };
 
        leds {
@@ -87,8 +48,4 @@
                        gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
                };
        };
-
-       aliases {
-               pip = &pip;
-       };
 };
diff --git a/arch/mips/boot/dts/cavium-octeon/dlink_dsr-500n-1000n.dtsi b/arch/mips/boot/dts/cavium-octeon/dlink_dsr-500n-1000n.dtsi
new file mode 100644 (file)
index 0000000..246b598
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Device tree source for D-Link DSR-500N/1000N (common parts).
+ *
+ * Written by: Aaro Koskinen <aaro.koskinen@iki.fi>
+ *
+ * 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/ "octeon_3xxx.dtsi"
+
+/ {
+       soc@0 {
+               smi0: mdio@1180000001800 {
+                       phy8: ethernet-phy@8 {
+                               reg = <8>;
+                               compatible = "ethernet-phy-ieee802.3-c22";
+                       };
+               };
+
+               pip: pip@11800a0000000 {
+                       interface@0 {
+                               ethernet@0 {
+                                       fixed-link {
+                                               speed = <1000>;
+                                               full-duplex;
+                                       };
+                               };
+                               ethernet@1 {
+                                       fixed-link {
+                                               speed = <1000>;
+                                               full-duplex;
+                                       };
+                               };
+                               ethernet@2 {
+                                       phy-handle = <&phy8>;
+                               };
+                       };
+               };
+
+               twsi0: i2c@1180000001000 {
+                       rtc@68 {
+                               compatible = "dallas,ds1337";
+                               reg = <0x68>;
+                       };
+               };
+
+               usbn: usbn@1180068000000 {
+                       refclk-frequency = <12000000>;
+                       refclk-type = "crystal";
+               };
+       };
+
+       aliases {
+               pip = &pip;
+       };
+};
diff --git a/arch/mips/boot/dts/cavium-octeon/dlink_dsr-500n.dts b/arch/mips/boot/dts/cavium-octeon/dlink_dsr-500n.dts
new file mode 100644 (file)
index 0000000..78886e1
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Device tree source for D-Link DSR-500N.
+ *
+ * Written by: Aaro Koskinen <aaro.koskinen@iki.fi>
+ *
+ * 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/ "dlink_dsr-500n-1000n.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+       model = "dlink,dsr-500n";
+       compatible = "dlink,dsr-500n", "cavium,octeon-3860";
+
+       soc@0 {
+               uart0: serial@1180000000800 {
+                       clock-frequency = <300000000>;
+               };
+       };
+
+       leds {
+               compatible = "gpio-leds";
+
+               usb {
+                       gpios = <&gpio 9 GPIO_ACTIVE_LOW>;
+               };
+
+               wps {
+                       gpios = <&gpio 11 GPIO_ACTIVE_LOW>;
+               };
+
+               wireless {
+                       label = "2.4g";
+                       gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
+               };
+       };
+};
index 144d776cc9f2aca50e0608a4716ff34e178af652..fcabd69b703012c5821481ebe6f58bbc024c8fc5 100644 (file)
@@ -1,5 +1,5 @@
 dtb-$(CONFIG_MIPS_MALTA)       += malta.dtb
-dtb-$(CONFIG_MIPS_SEAD3)       += sead3.dtb
+dtb-$(CONFIG_LEGACY_BOARD_SEAD3)       += sead3.dtb
 
 obj-y                          += $(patsubst %.dtb, %.dtb.o, $(dtb-y))
 
index b18c46637d21b4ae7b85636d98a3353332022bbc..f604a272d91dc61e6726e6ffd98f072923ab8c9e 100644 (file)
@@ -1,5 +1,8 @@
 /dts-v1/;
 
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/mips-gic.h>
+
 /memreserve/ 0x00000000 0x00001000;    /* YAMON exception vectors */
 /memreserve/ 0x00001000 0x000ef000;    /* YAMON */
 /memreserve/ 0x000f0000 0x00010000;    /* PIIX4 ISA memory */
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "mti,malta";
+
+       cpu_intc: interrupt-controller {
+               compatible = "mti,cpu-interrupt-controller";
+
+               interrupt-controller;
+               #interrupt-cells = <1>;
+       };
+
+       gic: interrupt-controller@1bdc0000 {
+               compatible = "mti,gic";
+               reg = <0x1bdc0000 0x20000>;
+
+               interrupt-controller;
+               #interrupt-cells = <3>;
+
+               /*
+                * Declare the interrupt-parent even though the mti,gic
+                * binding doesn't require it, such that the kernel can
+                * figure out that cpu_intc is the root interrupt
+                * controller & should be probed first.
+                */
+               interrupt-parent = <&cpu_intc>;
+
+               timer {
+                       compatible = "mti,gic-timer";
+                       interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+               };
+       };
+
+       i8259: interrupt-controller@20 {
+               compatible = "intel,i8259";
+
+               interrupt-controller;
+               #interrupt-cells = <1>;
+
+               interrupt-parent = <&gic>;
+               interrupts = <GIC_SHARED 3 IRQ_TYPE_LEVEL_HIGH>;
+       };
+
+       flash@1e000000 {
+               compatible = "intel,dt28f160", "cfi-flash";
+               reg = <0x1e000000 0x400000>;
+               bank-width = <4>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+
+               partitions {
+                       compatible = "fixed-partitions";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       yamon@0 {
+                               label = "YAMON";
+                               reg = <0x0 0x100000>;
+                               read-only;
+                       };
+
+                       user-fs@100000 {
+                               label = "User FS";
+                               reg = <0x100000 0x2e0000>;
+                       };
+
+                       board-config@3e0000 {
+                               label = "Board Config";
+                               reg = <0x3e0000 0x20000>;
+                               read-only;
+                       };
+               };
+       };
+
+       fpga_regs: system-controller@1f000000 {
+               compatible = "mti,malta-fpga", "syscon", "simple-mfd";
+               reg = <0x1f000000 0x1000>;
+
+               reboot {
+                       compatible = "syscon-reboot";
+                       regmap = <&fpga_regs>;
+                       offset = <0x500>;
+                       mask = <0x4d>;
+               };
+       };
+
+       isa {
+               compatible = "isa";
+               #address-cells = <2>;
+               #size-cells = <1>;
+               ranges = <1 0 0 0x1000>;
+
+               rtc@70 {
+                       compatible = "motorola,mc146818";
+                       reg = <1 0x70 0x8>;
+
+                       interrupt-parent = <&i8259>;
+                       interrupts = <8>;
+               };
+       };
 };
index e4b317d414f112576bbd04012381ab6804dabf86..b112879a5d9d30769c11568ca1c84986d83a9180 100644 (file)
@@ -4,10 +4,23 @@
 /memreserve/ 0x00001000 0x000ef000;    // ROM data
 /memreserve/ 0x000f0000 0x004cc000;    // reserved
 
+#include <dt-bindings/interrupt-controller/mips-gic.h>
+
 / {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "mti,sead-3";
+       model = "MIPS SEAD-3";
+       interrupt-parent = <&gic>;
+
+       chosen {
+               stdout-path = "uart1:115200";
+       };
+
+       aliases {
+               uart0 = &uart0;
+               uart1 = &uart1;
+       };
 
        cpus {
                cpu@0 {
                device_type = "memory";
                reg = <0x0 0x08000000>;
        };
+
+       cpu_intc: interrupt-controller {
+               compatible = "mti,cpu-interrupt-controller";
+
+               interrupt-controller;
+               #interrupt-cells = <1>;
+       };
+
+       gic: interrupt-controller@1b1c0000 {
+               compatible = "mti,gic";
+               reg = <0x1b1c0000 0x20000>;
+
+               interrupt-controller;
+               #interrupt-cells = <3>;
+
+               /*
+                * Declare the interrupt-parent even though the mti,gic
+                * binding doesn't require it, such that the kernel can
+                * figure out that cpu_intc is the root interrupt
+                * controller & should be probed first.
+                */
+               interrupt-parent = <&cpu_intc>;
+
+               timer {
+                       compatible = "mti,gic-timer";
+                       interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+               };
+       };
+
+       ehci@1b200000 {
+               compatible = "generic-ehci";
+               reg = <0x1b200000 0x1000>;
+
+               interrupts = <0>; /* GIC 0 or CPU 6 */
+
+               has-transaction-translator;
+       };
+
+       flash@1c000000 {
+               compatible = "intel,28f128j3", "cfi-flash";
+               reg = <0x1c000000 0x2000000>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               bank-width = <4>;
+
+               partitions {
+                       compatible = "fixed-partitions";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       user-fs@0 {
+                               label = "User FS";
+                               reg = <0x0 0x1fc0000>;
+                       };
+
+                       board-config@3e0000 {
+                               label = "Board Config";
+                               reg = <0x1fc0000 0x40000>;
+                       };
+               };
+       };
+
+       fpga_regs: system-controller@1f000000 {
+               compatible = "mti,sead3-fpga", "syscon", "simple-mfd";
+               reg = <0x1f000000 0x200>;
+
+               reboot {
+                       compatible = "syscon-reboot";
+                       regmap = <&fpga_regs>;
+                       offset = <0x50>;
+                       mask = <0x4d>;
+               };
+
+               poweroff {
+                       compatible = "restart-poweroff";
+               };
+       };
+
+       system-controller@1f000200 {
+               compatible = "mti,sead3-cpld", "syscon", "simple-mfd";
+               reg = <0x1f000200 0x300>;
+
+               led@10.0 {
+                       compatible = "register-bit-led";
+                       offset = <0x10>;
+                       mask = <0x1>;
+                       label = "pled0";
+               };
+               led@10.1 {
+                       compatible = "register-bit-led";
+                       offset = <0x10>;
+                       mask = <0x2>;
+                       label = "pled1";
+               };
+               led@10.2 {
+                       compatible = "register-bit-led";
+                       offset = <0x10>;
+                       mask = <0x4>;
+                       label = "pled2";
+               };
+               led@10.3 {
+                       compatible = "register-bit-led";
+                       offset = <0x10>;
+                       mask = <0x8>;
+                       label = "pled3";
+               };
+               led@10.4 {
+                       compatible = "register-bit-led";
+                       offset = <0x10>;
+                       mask = <0x10>;
+                       label = "pled4";
+               };
+               led@10.5 {
+                       compatible = "register-bit-led";
+                       offset = <0x10>;
+                       mask = <0x20>;
+                       label = "pled5";
+               };
+               led@10.6 {
+                       compatible = "register-bit-led";
+                       offset = <0x10>;
+                       mask = <0x40>;
+                       label = "pled6";
+               };
+               led@10.7 {
+                       compatible = "register-bit-led";
+                       offset = <0x10>;
+                       mask = <0x80>;
+                       label = "pled7";
+               };
+
+               led@18.0 {
+                       compatible = "register-bit-led";
+                       offset = <0x18>;
+                       mask = <0x1>;
+                       label = "fled0";
+               };
+               led@18.1 {
+                       compatible = "register-bit-led";
+                       offset = <0x18>;
+                       mask = <0x2>;
+                       label = "fled1";
+               };
+               led@18.2 {
+                       compatible = "register-bit-led";
+                       offset = <0x18>;
+                       mask = <0x4>;
+                       label = "fled2";
+               };
+               led@18.3 {
+                       compatible = "register-bit-led";
+                       offset = <0x18>;
+                       mask = <0x8>;
+                       label = "fled3";
+               };
+               led@18.4 {
+                       compatible = "register-bit-led";
+                       offset = <0x18>;
+                       mask = <0x10>;
+                       label = "fled4";
+               };
+               led@18.5 {
+                       compatible = "register-bit-led";
+                       offset = <0x18>;
+                       mask = <0x20>;
+                       label = "fled5";
+               };
+               led@18.6 {
+                       compatible = "register-bit-led";
+                       offset = <0x18>;
+                       mask = <0x40>;
+                       label = "fled6";
+               };
+               led@18.7 {
+                       compatible = "register-bit-led";
+                       offset = <0x18>;
+                       mask = <0x80>;
+                       label = "fled7";
+               };
+
+               lcd@200 {
+                       compatible = "mti,sead3-lcd";
+                       offset = <0x200>;
+               };
+       };
+
+       /* UART connected to FTDI & miniUSB socket */
+       uart0: uart@1f000900 {
+               compatible = "ns16550a";
+               reg = <0x1f000900 0x20>;
+               reg-io-width = <4>;
+               reg-shift = <2>;
+
+               clock-frequency = <14745600>;
+
+               interrupts = <3>; /* GIC 3 or CPU 4 */
+
+               no-loopback-test;
+       };
+
+       /* UART connected to RS232 socket */
+       uart1: uart@1f000800 {
+               compatible = "ns16550a";
+               reg = <0x1f000800 0x20>;
+               reg-io-width = <4>;
+               reg-shift = <2>;
+
+               clock-frequency = <14745600>;
+
+               interrupts = <2>; /* GIC 2 or CPU 4 */
+
+               no-loopback-test;
+       };
+
+       eth@1f010000 {
+               compatible = "smsc,lan9115";
+               reg = <0x1f010000 0x10000>;
+               reg-io-width = <4>;
+
+               interrupts = <0>; /* GIC 0 or CPU 6 */
+
+               phy-mode = "mii";
+               smsc,irq-push-pull;
+               smsc,save-mac-address;
+       };
 };
index ff49fc04500c7e993aef5cd618e8bfd1e7ef8e39..ab8362e04461ef2fd95e54aa7778b56c4dfe8dfb 100644 (file)
@@ -36,8 +36,6 @@
 
 #include <asm/octeon/cvmx-config.h>
 
-#include <asm/octeon/cvmx-mdio.h>
-
 #include <asm/octeon/cvmx-helper.h>
 #include <asm/octeon/cvmx-helper-util.h>
 #include <asm/octeon/cvmx-helper-board.h>
 #include <asm/octeon/cvmx-gmxx-defs.h>
 #include <asm/octeon/cvmx-asxx-defs.h>
 
-/**
- * cvmx_override_board_link_get(int ipd_port) is a function
- * pointer. It is meant to allow customization of the process of
- * talking to a PHY to determine link speed. It is called every
- * time a PHY must be polled for link status. Users should set
- * this pointer to a function before calling any cvmx-helper
- * operations.
- */
-cvmx_helper_link_info_t(*cvmx_override_board_link_get) (int ipd_port) =
-    NULL;
-
 /**
  * Return the MII PHY address associated with the given IPD
  * port. A result of -1 means there isn't a MII capable PHY
@@ -222,12 +209,6 @@ int cvmx_helper_board_get_mii_address(int ipd_port)
 cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port)
 {
        cvmx_helper_link_info_t result;
-       int phy_addr;
-       int is_broadcom_phy = 0;
-
-       /* Give the user a chance to override the processing of this function */
-       if (cvmx_override_board_link_get)
-               return cvmx_override_board_link_get(ipd_port);
 
        /* Unless we fix it later, all links are defaulted to down */
        result.u64 = 0;
@@ -263,8 +244,7 @@ cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port)
                        result.s.full_duplex = 1;
                        result.s.speed = 1000;
                        return result;
-               } else          /* The other port uses a broadcom PHY */
-                       is_broadcom_phy = 1;
+               }
                break;
        case CVMX_BOARD_TYPE_BBGW_REF:
                /* Port 1 on these boards is always Gigabit */
@@ -282,108 +262,7 @@ cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port)
                break;
        }
 
-       phy_addr = cvmx_helper_board_get_mii_address(ipd_port);
-       if (phy_addr != -1) {
-               if (is_broadcom_phy) {
-                       /*
-                        * Below we are going to read SMI/MDIO
-                        * register 0x19 which works on Broadcom
-                        * parts
-                        */
-                       int phy_status =
-                           cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                          0x19);
-                       switch ((phy_status >> 8) & 0x7) {
-                       case 0:
-                               result.u64 = 0;
-                               break;
-                       case 1:
-                               result.s.link_up = 1;
-                               result.s.full_duplex = 0;
-                               result.s.speed = 10;
-                               break;
-                       case 2:
-                               result.s.link_up = 1;
-                               result.s.full_duplex = 1;
-                               result.s.speed = 10;
-                               break;
-                       case 3:
-                               result.s.link_up = 1;
-                               result.s.full_duplex = 0;
-                               result.s.speed = 100;
-                               break;
-                       case 4:
-                               result.s.link_up = 1;
-                               result.s.full_duplex = 1;
-                               result.s.speed = 100;
-                               break;
-                       case 5:
-                               result.s.link_up = 1;
-                               result.s.full_duplex = 1;
-                               result.s.speed = 100;
-                               break;
-                       case 6:
-                               result.s.link_up = 1;
-                               result.s.full_duplex = 0;
-                               result.s.speed = 1000;
-                               break;
-                       case 7:
-                               result.s.link_up = 1;
-                               result.s.full_duplex = 1;
-                               result.s.speed = 1000;
-                               break;
-                       }
-               } else {
-                       /*
-                        * This code assumes we are using a Marvell
-                        * Gigabit PHY. All the speed information can
-                        * be read from register 17 in one
-                        * go. Somebody using a different PHY will
-                        * need to handle it above in the board
-                        * specific area.
-                        */
-                       int phy_status =
-                           cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17);
-
-                       /*
-                        * If the resolve bit 11 isn't set, see if
-                        * autoneg is turned off (bit 12, reg 0). The
-                        * resolve bit doesn't get set properly when
-                        * autoneg is off, so force it.
-                        */
-                       if ((phy_status & (1 << 11)) == 0) {
-                               int auto_status =
-                                   cvmx_mdio_read(phy_addr >> 8,
-                                                  phy_addr & 0xff, 0);
-                               if ((auto_status & (1 << 12)) == 0)
-                                       phy_status |= 1 << 11;
-                       }
-
-                       /*
-                        * Only return a link if the PHY has finished
-                        * auto negotiation and set the resolved bit
-                        * (bit 11)
-                        */
-                       if (phy_status & (1 << 11)) {
-                               result.s.link_up = 1;
-                               result.s.full_duplex = ((phy_status >> 13) & 1);
-                               switch ((phy_status >> 14) & 3) {
-                               case 0: /* 10 Mbps */
-                                       result.s.speed = 10;
-                                       break;
-                               case 1: /* 100 Mbps */
-                                       result.s.speed = 100;
-                                       break;
-                               case 2: /* 1 Gbps */
-                                       result.s.speed = 1000;
-                                       break;
-                               case 3: /* Illegal */
-                                       result.u64 = 0;
-                                       break;
-                               }
-                       }
-               }
-       } else if (OCTEON_IS_MODEL(OCTEON_CN3XXX)
+       if (OCTEON_IS_MODEL(OCTEON_CN3XXX)
                   || OCTEON_IS_MODEL(OCTEON_CN58XX)
                   || OCTEON_IS_MODEL(OCTEON_CN50XX)) {
                /*
@@ -432,176 +311,6 @@ cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port)
        return result;
 }
 
-/**
- * This function as a board specific method of changing the PHY
- * speed, duplex, and auto-negotiation. This programs the PHY and
- * not Octeon. This can be used to force Octeon's links to
- * specific settings.
- *
- * @phy_addr:  The address of the PHY to program
- * @enable_autoneg:
- *                 Non zero if you want to enable auto-negotiation.
- * @link_info: Link speed to program. If the speed is zero and auto-negotiation
- *                 is enabled, all possible negotiation speeds are advertised.
- *
- * Returns Zero on success, negative on failure
- */
-int cvmx_helper_board_link_set_phy(int phy_addr,
-                                  cvmx_helper_board_set_phy_link_flags_types_t
-                                  link_flags,
-                                  cvmx_helper_link_info_t link_info)
-{
-
-       /* Set the flow control settings based on link_flags */
-       if ((link_flags & set_phy_link_flags_flow_control_mask) !=
-           set_phy_link_flags_flow_control_dont_touch) {
-               cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
-               reg_autoneg_adver.u16 =
-                   cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                  CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
-               reg_autoneg_adver.s.asymmetric_pause =
-                   (link_flags & set_phy_link_flags_flow_control_mask) ==
-                   set_phy_link_flags_flow_control_enable;
-               reg_autoneg_adver.s.pause =
-                   (link_flags & set_phy_link_flags_flow_control_mask) ==
-                   set_phy_link_flags_flow_control_enable;
-               cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
-                               CVMX_MDIO_PHY_REG_AUTONEG_ADVER,
-                               reg_autoneg_adver.u16);
-       }
-
-       /* If speed isn't set and autoneg is on advertise all supported modes */
-       if ((link_flags & set_phy_link_flags_autoneg)
-           && (link_info.s.speed == 0)) {
-               cvmx_mdio_phy_reg_control_t reg_control;
-               cvmx_mdio_phy_reg_status_t reg_status;
-               cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
-               cvmx_mdio_phy_reg_extended_status_t reg_extended_status;
-               cvmx_mdio_phy_reg_control_1000_t reg_control_1000;
-
-               reg_status.u16 =
-                   cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                  CVMX_MDIO_PHY_REG_STATUS);
-               reg_autoneg_adver.u16 =
-                   cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                  CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
-               reg_autoneg_adver.s.advert_100base_t4 =
-                   reg_status.s.capable_100base_t4;
-               reg_autoneg_adver.s.advert_10base_tx_full =
-                   reg_status.s.capable_10_full;
-               reg_autoneg_adver.s.advert_10base_tx_half =
-                   reg_status.s.capable_10_half;
-               reg_autoneg_adver.s.advert_100base_tx_full =
-                   reg_status.s.capable_100base_x_full;
-               reg_autoneg_adver.s.advert_100base_tx_half =
-                   reg_status.s.capable_100base_x_half;
-               cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
-                               CVMX_MDIO_PHY_REG_AUTONEG_ADVER,
-                               reg_autoneg_adver.u16);
-               if (reg_status.s.capable_extended_status) {
-                       reg_extended_status.u16 =
-                           cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                          CVMX_MDIO_PHY_REG_EXTENDED_STATUS);
-                       reg_control_1000.u16 =
-                           cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                          CVMX_MDIO_PHY_REG_CONTROL_1000);
-                       reg_control_1000.s.advert_1000base_t_full =
-                           reg_extended_status.s.capable_1000base_t_full;
-                       reg_control_1000.s.advert_1000base_t_half =
-                           reg_extended_status.s.capable_1000base_t_half;
-                       cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
-                                       CVMX_MDIO_PHY_REG_CONTROL_1000,
-                                       reg_control_1000.u16);
-               }
-               reg_control.u16 =
-                   cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                  CVMX_MDIO_PHY_REG_CONTROL);
-               reg_control.s.autoneg_enable = 1;
-               reg_control.s.restart_autoneg = 1;
-               cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
-                               CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
-       } else if ((link_flags & set_phy_link_flags_autoneg)) {
-               cvmx_mdio_phy_reg_control_t reg_control;
-               cvmx_mdio_phy_reg_status_t reg_status;
-               cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
-               cvmx_mdio_phy_reg_control_1000_t reg_control_1000;
-
-               reg_status.u16 =
-                   cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                  CVMX_MDIO_PHY_REG_STATUS);
-               reg_autoneg_adver.u16 =
-                   cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                  CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
-               reg_autoneg_adver.s.advert_100base_t4 = 0;
-               reg_autoneg_adver.s.advert_10base_tx_full = 0;
-               reg_autoneg_adver.s.advert_10base_tx_half = 0;
-               reg_autoneg_adver.s.advert_100base_tx_full = 0;
-               reg_autoneg_adver.s.advert_100base_tx_half = 0;
-               if (reg_status.s.capable_extended_status) {
-                       reg_control_1000.u16 =
-                           cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                          CVMX_MDIO_PHY_REG_CONTROL_1000);
-                       reg_control_1000.s.advert_1000base_t_full = 0;
-                       reg_control_1000.s.advert_1000base_t_half = 0;
-               }
-               switch (link_info.s.speed) {
-               case 10:
-                       reg_autoneg_adver.s.advert_10base_tx_full =
-                           link_info.s.full_duplex;
-                       reg_autoneg_adver.s.advert_10base_tx_half =
-                           !link_info.s.full_duplex;
-                       break;
-               case 100:
-                       reg_autoneg_adver.s.advert_100base_tx_full =
-                           link_info.s.full_duplex;
-                       reg_autoneg_adver.s.advert_100base_tx_half =
-                           !link_info.s.full_duplex;
-                       break;
-               case 1000:
-                       reg_control_1000.s.advert_1000base_t_full =
-                           link_info.s.full_duplex;
-                       reg_control_1000.s.advert_1000base_t_half =
-                           !link_info.s.full_duplex;
-                       break;
-               }
-               cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
-                               CVMX_MDIO_PHY_REG_AUTONEG_ADVER,
-                               reg_autoneg_adver.u16);
-               if (reg_status.s.capable_extended_status)
-                       cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
-                                       CVMX_MDIO_PHY_REG_CONTROL_1000,
-                                       reg_control_1000.u16);
-               reg_control.u16 =
-                   cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                  CVMX_MDIO_PHY_REG_CONTROL);
-               reg_control.s.autoneg_enable = 1;
-               reg_control.s.restart_autoneg = 1;
-               cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
-                               CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
-       } else {
-               cvmx_mdio_phy_reg_control_t reg_control;
-               reg_control.u16 =
-                   cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
-                                  CVMX_MDIO_PHY_REG_CONTROL);
-               reg_control.s.autoneg_enable = 0;
-               reg_control.s.restart_autoneg = 1;
-               reg_control.s.duplex = link_info.s.full_duplex;
-               if (link_info.s.speed == 1000) {
-                       reg_control.s.speed_msb = 1;
-                       reg_control.s.speed_lsb = 0;
-               } else if (link_info.s.speed == 100) {
-                       reg_control.s.speed_msb = 0;
-                       reg_control.s.speed_lsb = 1;
-               } else if (link_info.s.speed == 10) {
-                       reg_control.s.speed_msb = 0;
-                       reg_control.s.speed_lsb = 0;
-               }
-               cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
-                               CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
-       }
-       return 0;
-}
-
 /**
  * This function is called by cvmx_helper_interface_probe() after it
  * determines the number of ports Octeon can support on a specific
@@ -675,48 +384,6 @@ int __cvmx_helper_board_hardware_enable(int interface)
                        cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface),
                                       0xc);
                }
-       } else if (cvmx_sysinfo_get()->board_type ==
-                  CVMX_BOARD_TYPE_CN3010_EVB_HS5) {
-               /*
-                * Broadcom PHYs require differnet ASX
-                * clocks. Unfortunately many boards don't define a
-                * new board Id and simply mangle the
-                * CN3010_EVB_HS5
-                */
-               if (interface == 0) {
-                       /*
-                        * Some boards use a hacked up bootloader that
-                        * identifies them as CN3010_EVB_HS5
-                        * evaluation boards.  This leads to all kinds
-                        * of configuration problems.  Detect one
-                        * case, and print warning, while trying to do
-                        * the right thing.
-                        */
-                       int phy_addr = cvmx_helper_board_get_mii_address(0);
-                       if (phy_addr != -1) {
-                               int phy_identifier =
-                                   cvmx_mdio_read(phy_addr >> 8,
-                                                  phy_addr & 0xff, 0x2);
-                               /* Is it a Broadcom PHY? */
-                               if (phy_identifier == 0x0143) {
-                                       cvmx_dprintf("\n");
-                                       cvmx_dprintf("ERROR:\n");
-                                       cvmx_dprintf
-                                           ("ERROR: Board type is CVMX_BOARD_TYPE_CN3010_EVB_HS5, but Broadcom PHY found.\n");
-                                       cvmx_dprintf
-                                           ("ERROR: The board type is mis-configured, and software malfunctions are likely.\n");
-                                       cvmx_dprintf
-                                           ("ERROR: All boards require a unique board type to identify them.\n");
-                                       cvmx_dprintf("ERROR:\n");
-                                       cvmx_dprintf("\n");
-                                       cvmx_wait(1000000000);
-                                       cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX
-                                                      (0, interface), 5);
-                                       cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX
-                                                      (0, interface), 5);
-                               }
-                       }
-               }
        } else if (cvmx_sysinfo_get()->board_type ==
                        CVMX_BOARD_TYPE_UBNT_E100) {
                cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), 0);
index f59c88ee9b31cb667b45fdaac54f931244c9d2cb..671ab1db272765ac50ad9aab1bb85a3ec548a8ef 100644 (file)
@@ -33,8 +33,6 @@
 
 #include <asm/octeon/cvmx-config.h>
 
-
-#include <asm/octeon/cvmx-mdio.h>
 #include <asm/octeon/cvmx-pko.h>
 #include <asm/octeon/cvmx-helper.h>
 #include <asm/octeon/cvmx-helper-board.h>
@@ -243,8 +241,7 @@ int __cvmx_helper_rgmii_enable(int interface)
        /* enable the ports now */
        for (port = 0; port < num_ports; port++) {
                union cvmx_gmxx_prtx_cfg gmx_cfg;
-               cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port
-                                         (interface, port));
+
                gmx_cfg.u64 =
                    cvmx_read_csr(CVMX_GMXX_PRTX_CFG(port, interface));
                gmx_cfg.s.en = 1;
index 6f9609e63a65af18bc36c69072a71b631e408216..54375340afe8ba4e3795fb41c7f7af749f0126d5 100644 (file)
@@ -34,7 +34,6 @@
 
 #include <asm/octeon/cvmx-config.h>
 
-#include <asm/octeon/cvmx-mdio.h>
 #include <asm/octeon/cvmx-helper.h>
 #include <asm/octeon/cvmx-helper-board.h>
 
index a56ee590de1f36b50a4f2f7bc962f01e224bc141..d347fe13b66646121649af57a4fa8854319dd2f3 100644 (file)
@@ -234,8 +234,6 @@ int __cvmx_helper_xaui_enable(int interface)
        cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), gmx_tx_int_en.u64);
        cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), pcsx_int_en_reg.u64);
 
-       cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port(interface, 0));
-
        /* (8) Enable packet reception */
        xauiMiscCtl.s.gmxeno = 0;
        cvmx_write_csr(CVMX_PCSXX_MISC_CTL_REG(interface), xauiMiscCtl.u64);
index ff26d0217b878feb1aca373c588337af96af8431..6456af6424719a5cc1adabaea0fc1ccd210c29df 100644 (file)
@@ -841,7 +841,6 @@ int __cvmx_helper_errata_fix_ipd_ptr_alignment(void)
        int retry_cnt;
        int retry_loop_cnt;
        int i;
-       cvmx_helper_link_info_t link_info;
 
        /* Save values for restore at end */
        uint64_t prtx_cfg =
@@ -1002,15 +1001,6 @@ fix_ipd_exit:
                       (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)),
                       frame_max);
        cvmx_write_csr(CVMX_ASXX_PRT_LOOP(INTERFACE(FIX_IPD_OUTPORT)), 0);
-       /* Set link to down so autonegotiation will set it up again */
-       link_info.u64 = 0;
-       cvmx_helper_link_set(FIX_IPD_OUTPORT, link_info);
-
-       /*
-        * Bring the link back up as autonegotiation is not done in
-        * user applications.
-        */
-       cvmx_helper_link_autoconf(FIX_IPD_OUTPORT);
 
        CVMX_SYNC;
        if (num_segs)
index 5537f95b28c9be169ffd42a42094eaeae68ce064..9a2db1c013d92e548fd04ef0a008ec442942350c 100644 (file)
@@ -65,7 +65,8 @@ EXPORT_SYMBOL(octeon_should_swizzle_table);
 extern void pci_console_init(const char *arg);
 #endif
 
-static unsigned long long MAX_MEMORY = 512ull << 20;
+static unsigned long long max_memory = ULLONG_MAX;
+static unsigned long long reserve_low_mem;
 
 DEFINE_SEMAPHORE(octeon_bootbus_sem);
 EXPORT_SYMBOL(octeon_bootbus_sem);
@@ -75,7 +76,6 @@ struct octeon_boot_descriptor *octeon_boot_desc_ptr;
 struct cvmx_bootinfo *octeon_bootinfo;
 EXPORT_SYMBOL(octeon_bootinfo);
 
-static unsigned long long RESERVE_LOW_MEM = 0ull;
 #ifdef CONFIG_KEXEC
 #ifdef CONFIG_SMP
 /*
@@ -125,18 +125,18 @@ static void kexec_bootmem_init(uint64_t mem_size, uint32_t low_reserved_bytes)
        bootmem_desc->major_version = CVMX_BOOTMEM_DESC_MAJ_VER;
        bootmem_desc->minor_version = CVMX_BOOTMEM_DESC_MIN_VER;
 
-       addr = (OCTEON_DDR0_BASE + RESERVE_LOW_MEM + low_reserved_bytes);
+       addr = (OCTEON_DDR0_BASE + reserve_low_mem + low_reserved_bytes);
        bootmem_desc->head_addr = 0;
 
        if (mem_size <= OCTEON_DDR0_SIZE) {
                __cvmx_bootmem_phy_free(addr,
-                               mem_size - RESERVE_LOW_MEM -
+                               mem_size - reserve_low_mem -
                                low_reserved_bytes, 0);
                return;
        }
 
        __cvmx_bootmem_phy_free(addr,
-                       OCTEON_DDR0_SIZE - RESERVE_LOW_MEM -
+                       OCTEON_DDR0_SIZE - reserve_low_mem -
                        low_reserved_bytes, 0);
 
        mem_size -= OCTEON_DDR0_SIZE;
@@ -857,15 +857,15 @@ void __init prom_init(void)
 
        /* Default to 64MB in the simulator to speed things up */
        if (octeon_is_simulation())
-               MAX_MEMORY = 64ull << 20;
+               max_memory = 64ull << 20;
 
        arg = strstr(arcs_cmdline, "mem=");
        if (arg) {
-               MAX_MEMORY = memparse(arg + 4, &p);
-               if (MAX_MEMORY == 0)
-                       MAX_MEMORY = 32ull << 30;
+               max_memory = memparse(arg + 4, &p);
+               if (max_memory == 0)
+                       max_memory = 32ull << 30;
                if (*p == '@')
-                       RESERVE_LOW_MEM = memparse(p + 1, &p);
+                       reserve_low_mem = memparse(p + 1, &p);
        }
 
        arcs_cmdline[0] = 0;
@@ -875,11 +875,11 @@ void __init prom_init(void)
                        cvmx_phys_to_ptr(octeon_boot_desc_ptr->argv[i]);
                if ((strncmp(arg, "MEM=", 4) == 0) ||
                    (strncmp(arg, "mem=", 4) == 0)) {
-                       MAX_MEMORY = memparse(arg + 4, &p);
-                       if (MAX_MEMORY == 0)
-                               MAX_MEMORY = 32ull << 30;
+                       max_memory = memparse(arg + 4, &p);
+                       if (max_memory == 0)
+                               max_memory = 32ull << 30;
                        if (*p == '@')
-                               RESERVE_LOW_MEM = memparse(p + 1, &p);
+                               reserve_low_mem = memparse(p + 1, &p);
 #ifdef CONFIG_KEXEC
                } else if (strncmp(arg, "crashkernel=", 12) == 0) {
                        crashk_size = memparse(arg+12, &p);
@@ -971,13 +971,13 @@ void __init plat_mem_setup(void)
         * to consistently work.
         */
        mem_alloc_size = 4 << 20;
-       if (mem_alloc_size > MAX_MEMORY)
-               mem_alloc_size = MAX_MEMORY;
+       if (mem_alloc_size > max_memory)
+               mem_alloc_size = max_memory;
 
 /* Crashkernel ignores bootmem list. It relies on mem=X@Y option */
 #ifdef CONFIG_CRASH_DUMP
-       add_memory_region(RESERVE_LOW_MEM, MAX_MEMORY, BOOT_MEM_RAM);
-       total += MAX_MEMORY;
+       add_memory_region(reserve_low_mem, max_memory, BOOT_MEM_RAM);
+       total += max_memory;
 #else
 #ifdef CONFIG_KEXEC
        if (crashk_size > 0) {
@@ -992,7 +992,7 @@ void __init plat_mem_setup(void)
         */
        cvmx_bootmem_lock();
        while ((boot_mem_map.nr_map < BOOT_MEM_MAP_MAX)
-               && (total < MAX_MEMORY)) {
+               && (total < max_memory)) {
                memory = cvmx_bootmem_phy_alloc(mem_alloc_size,
                                                __pa_symbol(&_end), -1,
                                                0x100000,
diff --git a/arch/mips/configs/generic/32r1.config b/arch/mips/configs/generic/32r1.config
new file mode 100644 (file)
index 0000000..a11cd87
--- /dev/null
@@ -0,0 +1,2 @@
+CONFIG_CPU_MIPS32_R1=y
+CONFIG_HIGHMEM=y
diff --git a/arch/mips/configs/generic/32r2.config b/arch/mips/configs/generic/32r2.config
new file mode 100644 (file)
index 0000000..9570672
--- /dev/null
@@ -0,0 +1,3 @@
+CONFIG_CPU_MIPS32_R2=y
+CONFIG_MIPS_O32_FP64_SUPPORT=y
+CONFIG_HIGHMEM=y
diff --git a/arch/mips/configs/generic/32r6.config b/arch/mips/configs/generic/32r6.config
new file mode 100644 (file)
index 0000000..ca606e7
--- /dev/null
@@ -0,0 +1,2 @@
+CONFIG_CPU_MIPS32_R6=y
+CONFIG_HIGHMEM=y
diff --git a/arch/mips/configs/generic/64r1.config b/arch/mips/configs/generic/64r1.config
new file mode 100644 (file)
index 0000000..7c1ea7e
--- /dev/null
@@ -0,0 +1,4 @@
+CONFIG_CPU_MIPS64_R1=y
+CONFIG_64BIT=y
+CONFIG_MIPS32_O32=y
+CONFIG_MIPS32_N32=y
diff --git a/arch/mips/configs/generic/64r2.config b/arch/mips/configs/generic/64r2.config
new file mode 100644 (file)
index 0000000..b4d31ae
--- /dev/null
@@ -0,0 +1,5 @@
+CONFIG_CPU_MIPS64_R2=y
+CONFIG_MIPS_O32_FP64_SUPPORT=y
+CONFIG_64BIT=y
+CONFIG_MIPS32_O32=y
+CONFIG_MIPS32_N32=y
diff --git a/arch/mips/configs/generic/64r6.config b/arch/mips/configs/generic/64r6.config
new file mode 100644 (file)
index 0000000..7cac033
--- /dev/null
@@ -0,0 +1,4 @@
+CONFIG_CPU_MIPS64_R6=y
+CONFIG_64BIT=y
+CONFIG_MIPS32_O32=y
+CONFIG_MIPS32_N32=y
diff --git a/arch/mips/configs/generic/board-sead-3.config b/arch/mips/configs/generic/board-sead-3.config
new file mode 100644 (file)
index 0000000..3b5e1ac
--- /dev/null
@@ -0,0 +1,32 @@
+CONFIG_LEGACY_BOARD_SEAD3=y
+
+CONFIG_AUXDISPLAY=y
+CONFIG_IMG_ASCII_LCD=y
+
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_SYSCON=y
+
+CONFIG_MMC=y
+CONFIG_MMC_SPI=y
+
+CONFIG_MTD=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_OF_PARTS=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_GLUEBI=y
+
+CONFIG_NETDEVICES=y
+CONFIG_SMSC911X=y
+CONFIG_SMSC_PHY=y
+
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+
+CONFIG_USB=y
+CONFIG_USB_EHCI_HCD=y
diff --git a/arch/mips/configs/generic/eb.config b/arch/mips/configs/generic/eb.config
new file mode 100644 (file)
index 0000000..c5cdc99
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_CPU_BIG_ENDIAN=y
diff --git a/arch/mips/configs/generic/el.config b/arch/mips/configs/generic/el.config
new file mode 100644 (file)
index 0000000..ee43fdb
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_CPU_LITTLE_ENDIAN=y
diff --git a/arch/mips/configs/generic/micro32r2.config b/arch/mips/configs/generic/micro32r2.config
new file mode 100644 (file)
index 0000000..b701fe7
--- /dev/null
@@ -0,0 +1,4 @@
+CONFIG_CPU_MIPS32_R2=y
+CONFIG_CPU_MICROMIPS=y
+CONFIG_MIPS_O32_FP64_SUPPORT=y
+CONFIG_HIGHMEM=y
diff --git a/arch/mips/configs/generic_defconfig b/arch/mips/configs/generic_defconfig
new file mode 100644 (file)
index 0000000..c95d94c
--- /dev/null
@@ -0,0 +1,96 @@
+CONFIG_MIPS_GENERIC=y
+CONFIG_CPU_LITTLE_ENDIAN=y
+CONFIG_MIPS_CPS=y
+CONFIG_CPU_HAS_MSA=y
+CONFIG_HIGHMEM=y
+CONFIG_NR_CPUS=2
+CONFIG_MIPS_O32_FP64_SUPPORT=y
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_USERFAULTFD=y
+CONFIG_EMBEDDED=y
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_COMPAT_BRK is not set
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_TRIM_UNUSED_KSYMS=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_NETFILTER=y
+# CONFIG_WIRELESS is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_SCSI=y
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_HWMON is not set
+CONFIG_MFD_SYSCON=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MIPS_PLATFORM_DEVICES is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_FANOTIFY=y
+CONFIG_FUSE_FS=y
+CONFIG_CUSE=y
+CONFIG_OVERLAY_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_REDUCED=y
+CONFIG_DEBUG_FS=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_FTRACE is not set
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="earlycon"
+# CONFIG_XZ_DEC_X86 is not set
+# CONFIG_XZ_DEC_POWERPC is not set
+# CONFIG_XZ_DEC_IA64 is not set
+# CONFIG_XZ_DEC_ARM is not set
+# CONFIG_XZ_DEC_ARMTHUMB is not set
+# CONFIG_XZ_DEC_SPARC is not set
diff --git a/arch/mips/configs/loongson1c_defconfig b/arch/mips/configs/loongson1c_defconfig
new file mode 100644 (file)
index 0000000..2304d41
--- /dev/null
@@ -0,0 +1,126 @@
+CONFIG_MACH_LOONGSON32=y
+CONFIG_LOONGSON1_LS1C=y
+CONFIG_PREEMPT=y
+# CONFIG_SECCOMP is not set
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_KERNEL_XZ=y
+CONFIG_SYSVIPC=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_NAMESPACES=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_EXPERT=y
+CONFIG_PERF_EVENTS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_LOONGSON1=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_SCSI=m
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=m
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+CONFIG_STMMAC_ETH=y
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_WLAN is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_LEGACY_PTY_COUNT=8
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_LOONGSON1=y
+# CONFIG_HWMON is not set
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_HID_GENERIC=m
+CONFIG_USB_HID=m
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_STORAGE=m
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_LOONGSON1=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_UBIFS_FS=y
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+CONFIG_UBIFS_ATIME_SUPPORT=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_DYNAMIC_DEBUG=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
+# CONFIG_EARLY_PRINTK is not set
+# CONFIG_CRYPTO_ECHAINIV is not set
+# CONFIG_CRYPTO_HW is not set
index 5afb4840aec75c3d41fb7a4fd08b8322d0437904..58d43f3c348d00fec15c975afbf2aa0693879385 100644 (file)
@@ -230,7 +230,7 @@ CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
 CONFIG_MTD_CFI_AMDSTD=y
 CONFIG_MTD_CFI_STAA=y
-CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
 CONFIG_MTD_UBI=m
 CONFIG_MTD_UBI_GLUEBI=m
 CONFIG_BLK_DEV_FD=m
@@ -318,6 +318,8 @@ CONFIG_LIBERTAS=m
 # CONFIG_SERIO_I8042 is not set
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
 # CONFIG_HWMON is not set
 CONFIG_FB=y
 CONFIG_FB_CIRRUS=y
index 98f13879bb8fda832e3e73355f96af7defdcbc23..c8f7e2835840ddb5737008004be064d289c6182a 100644 (file)
@@ -235,7 +235,7 @@ CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
 CONFIG_MTD_CFI_AMDSTD=y
 CONFIG_MTD_CFI_STAA=y
-CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
 CONFIG_MTD_UBI=m
 CONFIG_MTD_UBI_GLUEBI=m
 CONFIG_BLK_DEV_FD=m
@@ -331,6 +331,8 @@ CONFIG_LIBERTAS=m
 # CONFIG_SERIO_I8042 is not set
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
 # CONFIG_HWMON is not set
 CONFIG_FB=y
 CONFIG_FB_CIRRUS=y
index 3b5d5913f548cddaf65f457da86ae245dee9f06d..d2f54e55356c8cfb427b7f6ca896110f16b205b9 100644 (file)
@@ -234,7 +234,7 @@ CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
 CONFIG_MTD_CFI_AMDSTD=y
 CONFIG_MTD_CFI_STAA=y
-CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
 CONFIG_MTD_UBI=m
 CONFIG_MTD_UBI_GLUEBI=m
 CONFIG_BLK_DEV_FD=m
@@ -331,6 +331,8 @@ CONFIG_LIBERTAS=m
 # CONFIG_SERIO_I8042 is not set
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
 # CONFIG_HWMON is not set
 CONFIG_FB=y
 CONFIG_FB_CIRRUS=y
index 65f140e1e872a87c8aeb1728c71b710889e25010..cbf37dd0c4908a386c3257a0b084eae95435dc17 100644 (file)
@@ -132,6 +132,8 @@ CONFIG_LEGACY_PTY_COUNT=4
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_HW_RANDOM=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
 # CONFIG_HWMON is not set
 CONFIG_FB=y
 CONFIG_FIRMWARE_EDID=y
index 799c4338fd5e0580d1810e44a9e8a85361475705..35f6ba260df8fe215c058577377c2a96fcfbfc6b 100644 (file)
@@ -132,6 +132,8 @@ CONFIG_LEGACY_PTY_COUNT=16
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_HW_RANDOM=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
 # CONFIG_HWMON is not set
 CONFIG_VIDEO_OUTPUT_CONTROL=m
 CONFIG_FB=y
index ac0eb4daf1010b86cff9c2759fa7de046ce39996..900f14543eeb9856d75468b432161c53c0c88de2 100644 (file)
@@ -134,6 +134,8 @@ CONFIG_LEGACY_PTY_COUNT=4
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_HW_RANDOM=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
 # CONFIG_HWMON is not set
 CONFIG_FB=y
 CONFIG_FIRMWARE_EDID=y
index 31846000530fb158bec7131def3dd40249394d5f..8e2738b5e180a7a5ea9873f52b86d65312ce5f59 100644 (file)
@@ -137,6 +137,8 @@ CONFIG_LEGACY_PTY_COUNT=4
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_HW_RANDOM=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
 # CONFIG_HWMON is not set
 CONFIG_VIDEO_OUTPUT_CONTROL=m
 CONFIG_FB=y
index a79107da0675b881cbc704863692428f2555f6a0..6dc4e309a6918c69bd06277c7192e065c6e5c5ce 100644 (file)
@@ -131,6 +131,8 @@ CONFIG_LEGACY_PTY_COUNT=16
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_HW_RANDOM=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
 # CONFIG_HWMON is not set
 CONFIG_VIDEO_OUTPUT_CONTROL=m
 CONFIG_FB=y
index 73221573275166e1ae7d044b1bad8dfa1b1f6d65..3d0d9cb9673f80d976033e969584308d92d3f446 100644 (file)
@@ -231,7 +231,7 @@ CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
 CONFIG_MTD_CFI_AMDSTD=y
 CONFIG_MTD_CFI_STAA=y
-CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
 CONFIG_MTD_UBI=m
 CONFIG_MTD_UBI_GLUEBI=m
 CONFIG_BLK_DEV_FD=m
@@ -326,6 +326,8 @@ CONFIG_LIBERTAS=m
 # CONFIG_SERIO_I8042 is not set
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
 # CONFIG_HWMON is not set
 CONFIG_FB=y
 CONFIG_FB_CIRRUS=y
index 8b7429127a1d18c192677e7b04d7d9c68e6e9875..7d32fbbca96269dd37d22d672ee2a6a6783dfc32 100644 (file)
@@ -29,7 +29,6 @@ CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_EMBEDDED=y
 # CONFIG_COMPAT_BRK is not set
 CONFIG_PROFILING=y
-CONFIG_CC_STACKPROTECTOR_STRONG=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
@@ -264,7 +263,6 @@ CONFIG_DMADEVICES=y
 CONFIG_IMG_MDC_DMA=y
 CONFIG_STAGING=y
 CONFIG_ASHMEM=y
-# CONFIG_ANDROID_TIMED_OUTPUT is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_MEMORY=y
 CONFIG_IIO=y
diff --git a/arch/mips/configs/sead3_defconfig b/arch/mips/configs/sead3_defconfig
deleted file mode 100644 (file)
index dae9354..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-CONFIG_MIPS_SEAD3=y
-CONFIG_CPU_LITTLE_ENDIAN=y
-CONFIG_CPU_MIPS32_R2=y
-CONFIG_HZ_100=y
-CONFIG_SYSVIPC=y
-CONFIG_POSIX_MQUEUE=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=15
-CONFIG_EMBEDDED=y
-CONFIG_SLAB=y
-CONFIG_PROFILING=y
-CONFIG_OPROFILE=y
-CONFIG_MODULES=y
-# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_NET=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_INET=y
-CONFIG_IP_PNP=y
-CONFIG_IP_PNP_DHCP=y
-CONFIG_IP_PNP_BOOTP=y
-# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
-# CONFIG_INET_XFRM_MODE_TUNNEL is not set
-# CONFIG_INET_XFRM_MODE_BEET is not set
-# CONFIG_INET_LRO is not set
-# CONFIG_INET_DIAG is not set
-# CONFIG_IPV6 is not set
-# CONFIG_WIRELESS is not set
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_DEVTMPFS=y
-CONFIG_MTD=y
-CONFIG_MTD_BLOCK=y
-CONFIG_MTD_CFI=y
-CONFIG_MTD_CFI_INTELEXT=y
-CONFIG_MTD_PHYSMAP=y
-CONFIG_MTD_UBI=y
-CONFIG_MTD_UBI_GLUEBI=y
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_CRYPTOLOOP=m
-CONFIG_SCSI=y
-# CONFIG_SCSI_PROC_FS is not set
-CONFIG_BLK_DEV_SD=y
-CONFIG_CHR_DEV_SG=y
-# CONFIG_SCSI_LOWLEVEL is not set
-CONFIG_NETDEVICES=y
-CONFIG_SMSC911X=y
-# CONFIG_NET_VENDOR_WIZNET is not set
-CONFIG_MARVELL_PHY=y
-CONFIG_DAVICOM_PHY=y
-CONFIG_QSEMI_PHY=y
-CONFIG_LXT_PHY=y
-CONFIG_CICADA_PHY=y
-CONFIG_VITESSE_PHY=y
-CONFIG_SMSC_PHY=y
-CONFIG_BROADCOM_PHY=y
-CONFIG_ICPLUS_PHY=y
-# CONFIG_WLAN is not set
-# CONFIG_INPUT_MOUSEDEV is not set
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_SERIO is not set
-# CONFIG_CONSOLE_TRANSLATIONS is not set
-CONFIG_VT_HW_CONSOLE_BINDING=y
-CONFIG_LEGACY_PTY_COUNT=32
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_8250_NR_UARTS=2
-CONFIG_SERIAL_8250_RUNTIME_UARTS=2
-# CONFIG_HW_RANDOM is not set
-CONFIG_I2C=y
-# CONFIG_I2C_COMPAT is not set
-CONFIG_I2C_CHARDEV=y
-# CONFIG_I2C_HELPER_AUTO is not set
-CONFIG_SPI=y
-CONFIG_SENSORS_ADT7475=y
-CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_LCD_CLASS_DEVICE=y
-CONFIG_BACKLIGHT_CLASS_DEVICE=y
-# CONFIG_VGA_CONSOLE is not set
-CONFIG_USB=y
-CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
-CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_EHCI_ROOT_HUB_TT=y
-CONFIG_USB_STORAGE=y
-CONFIG_MMC=y
-CONFIG_MMC_DEBUG=y
-CONFIG_MMC_SPI=y
-CONFIG_NEW_LEDS=y
-CONFIG_LEDS_CLASS=y
-CONFIG_LEDS_TRIGGERS=y
-CONFIG_LEDS_TRIGGER_HEARTBEAT=y
-CONFIG_RTC_CLASS=y
-CONFIG_RTC_DRV_M41T80=y
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_XFS_FS=y
-CONFIG_XFS_QUOTA=y
-CONFIG_XFS_POSIX_ACL=y
-CONFIG_QUOTA=y
-# CONFIG_PRINT_QUOTA_WARNING is not set
-CONFIG_MSDOS_FS=m
-CONFIG_VFAT_FS=m
-CONFIG_TMPFS=y
-CONFIG_JFFS2_FS=y
-CONFIG_NFS_FS=y
-CONFIG_ROOT_NFS=y
-CONFIG_NLS_CODEPAGE_437=y
-CONFIG_NLS_ASCII=y
-CONFIG_NLS_ISO8859_1=y
-CONFIG_NLS_ISO8859_15=y
-CONFIG_NLS_UTF8=y
-# CONFIG_FTRACE is not set
-CONFIG_CRYPTO_CBC=y
-CONFIG_CRYPTO_ECB=y
-CONFIG_CRYPTO_ARC4=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
-# CONFIG_CRYPTO_HW is not set
diff --git a/arch/mips/configs/sead3micro_defconfig b/arch/mips/configs/sead3micro_defconfig
deleted file mode 100644 (file)
index cd91a77..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-CONFIG_MIPS_SEAD3=y
-CONFIG_CPU_LITTLE_ENDIAN=y
-CONFIG_CPU_MIPS32_R2=y
-CONFIG_CPU_MICROMIPS=y
-CONFIG_HZ_100=y
-CONFIG_SYSVIPC=y
-CONFIG_POSIX_MQUEUE=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=15
-CONFIG_EMBEDDED=y
-CONFIG_SLAB=y
-CONFIG_PROFILING=y
-CONFIG_OPROFILE=y
-CONFIG_MODULES=y
-# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_NET=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_INET=y
-CONFIG_IP_PNP=y
-CONFIG_IP_PNP_DHCP=y
-CONFIG_IP_PNP_BOOTP=y
-# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
-# CONFIG_INET_XFRM_MODE_TUNNEL is not set
-# CONFIG_INET_XFRM_MODE_BEET is not set
-# CONFIG_INET_LRO is not set
-# CONFIG_INET_DIAG is not set
-# CONFIG_IPV6 is not set
-# CONFIG_WIRELESS is not set
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_DEVTMPFS=y
-CONFIG_MTD=y
-CONFIG_MTD_BLOCK=y
-CONFIG_MTD_CFI=y
-CONFIG_MTD_CFI_INTELEXT=y
-CONFIG_MTD_PHYSMAP=y
-CONFIG_MTD_UBI=y
-CONFIG_MTD_UBI_GLUEBI=y
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_CRYPTOLOOP=m
-CONFIG_SCSI=y
-# CONFIG_SCSI_PROC_FS is not set
-CONFIG_BLK_DEV_SD=y
-CONFIG_CHR_DEV_SG=y
-# CONFIG_SCSI_LOWLEVEL is not set
-CONFIG_NETDEVICES=y
-CONFIG_SMSC911X=y
-# CONFIG_NET_VENDOR_WIZNET is not set
-CONFIG_MARVELL_PHY=y
-CONFIG_DAVICOM_PHY=y
-CONFIG_QSEMI_PHY=y
-CONFIG_LXT_PHY=y
-CONFIG_CICADA_PHY=y
-CONFIG_VITESSE_PHY=y
-CONFIG_SMSC_PHY=y
-CONFIG_BROADCOM_PHY=y
-CONFIG_ICPLUS_PHY=y
-# CONFIG_WLAN is not set
-# CONFIG_INPUT_MOUSEDEV is not set
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_SERIO is not set
-# CONFIG_CONSOLE_TRANSLATIONS is not set
-CONFIG_VT_HW_CONSOLE_BINDING=y
-CONFIG_LEGACY_PTY_COUNT=32
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_8250_NR_UARTS=2
-CONFIG_SERIAL_8250_RUNTIME_UARTS=2
-# CONFIG_HW_RANDOM is not set
-CONFIG_I2C=y
-# CONFIG_I2C_COMPAT is not set
-CONFIG_I2C_CHARDEV=y
-# CONFIG_I2C_HELPER_AUTO is not set
-CONFIG_SPI=y
-CONFIG_SENSORS_ADT7475=y
-CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_LCD_CLASS_DEVICE=y
-CONFIG_BACKLIGHT_CLASS_DEVICE=y
-# CONFIG_VGA_CONSOLE is not set
-CONFIG_USB=y
-CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
-CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_EHCI_ROOT_HUB_TT=y
-CONFIG_USB_STORAGE=y
-CONFIG_MMC=y
-CONFIG_MMC_DEBUG=y
-CONFIG_MMC_SPI=y
-CONFIG_NEW_LEDS=y
-CONFIG_LEDS_CLASS=y
-CONFIG_LEDS_TRIGGERS=y
-CONFIG_LEDS_TRIGGER_HEARTBEAT=y
-CONFIG_RTC_CLASS=y
-CONFIG_RTC_DRV_M41T80=y
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_XFS_FS=y
-CONFIG_XFS_QUOTA=y
-CONFIG_XFS_POSIX_ACL=y
-CONFIG_QUOTA=y
-# CONFIG_PRINT_QUOTA_WARNING is not set
-CONFIG_MSDOS_FS=m
-CONFIG_VFAT_FS=m
-CONFIG_TMPFS=y
-CONFIG_JFFS2_FS=y
-CONFIG_NFS_FS=y
-CONFIG_ROOT_NFS=y
-CONFIG_NLS_CODEPAGE_437=y
-CONFIG_NLS_ASCII=y
-CONFIG_NLS_ISO8859_1=y
-CONFIG_NLS_ISO8859_15=y
-CONFIG_NLS_UTF8=y
-# CONFIG_FTRACE is not set
-CONFIG_CRYPTO_CBC=y
-CONFIG_CRYPTO_ECB=y
-CONFIG_CRYPTO_ARC4=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
-# CONFIG_CRYPTO_HW is not set
diff --git a/arch/mips/generic/Kconfig b/arch/mips/generic/Kconfig
new file mode 100644 (file)
index 0000000..a606b3f
--- /dev/null
@@ -0,0 +1,19 @@
+if MIPS_GENERIC
+
+config LEGACY_BOARDS
+       bool
+       help
+         Select this from your board if the board must use a legacy, non-UHI,
+         boot protocol. This will cause the kernel to scan through the list of
+         supported machines calling their detect functions in turn if the
+         kernel is booted without being provided with an FDT via the UHI
+         boot protocol.
+
+config LEGACY_BOARD_SEAD3
+       bool "Support MIPS SEAD-3 boards"
+       select LEGACY_BOARDS
+       help
+         Enable this to include support for booting on MIPS SEAD-3 FPGA-based
+         development boards, which boot using a legacy boot protocol.
+
+endif
diff --git a/arch/mips/generic/Makefile b/arch/mips/generic/Makefile
new file mode 100644 (file)
index 0000000..7c66494
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# Copyright (C) 2016 Imagination Technologies
+# Author: Paul Burton <paul.burton@imgtec.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.
+#
+
+obj-y += init.o
+obj-y += irq.o
+obj-y += proc.o
+
+obj-$(CONFIG_LEGACY_BOARD_SEAD3)       += board-sead3.o
diff --git a/arch/mips/generic/Platform b/arch/mips/generic/Platform
new file mode 100644 (file)
index 0000000..9a30d69
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Copyright (C) 2016 Imagination Technologies
+# Author: Paul Burton <paul.burton@imgtec.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.
+#
+
+platform-$(CONFIG_MIPS_GENERIC)        += generic/
+cflags-$(CONFIG_MIPS_GENERIC)  += -I$(srctree)/arch/mips/include/asm/mach-generic
+load-$(CONFIG_MIPS_GENERIC)    += 0xffffffff80100000
+all-$(CONFIG_MIPS_GENERIC)     := vmlinux.gz.itb
diff --git a/arch/mips/generic/board-sead3.c b/arch/mips/generic/board-sead3.c
new file mode 100644 (file)
index 0000000..f4ae058
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.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) "sead3: " fmt
+
+#include <linux/errno.h>
+#include <linux/libfdt.h>
+#include <linux/printk.h>
+
+#include <asm/fw/fw.h>
+#include <asm/io.h>
+#include <asm/machine.h>
+
+#define SEAD_CONFIG                    CKSEG1ADDR(0x1b100110)
+#define SEAD_CONFIG_GIC_PRESENT                BIT(1)
+
+#define MIPS_REVISION                  CKSEG1ADDR(0x1fc00010)
+#define MIPS_REVISION_MACHINE          (0xf << 4)
+#define MIPS_REVISION_MACHINE_SEAD3    (0x4 << 4)
+
+static __init bool sead3_detect(void)
+{
+       uint32_t rev;
+
+       rev = __raw_readl((void *)MIPS_REVISION);
+       return (rev & MIPS_REVISION_MACHINE) == MIPS_REVISION_MACHINE_SEAD3;
+}
+
+static __init int append_cmdline(void *fdt)
+{
+       int err, chosen_off;
+
+       /* find or add chosen node */
+       chosen_off = fdt_path_offset(fdt, "/chosen");
+       if (chosen_off == -FDT_ERR_NOTFOUND)
+               chosen_off = fdt_path_offset(fdt, "/chosen@0");
+       if (chosen_off == -FDT_ERR_NOTFOUND)
+               chosen_off = fdt_add_subnode(fdt, 0, "chosen");
+       if (chosen_off < 0) {
+               pr_err("Unable to find or add DT chosen node: %d\n",
+                      chosen_off);
+               return chosen_off;
+       }
+
+       err = fdt_setprop_string(fdt, chosen_off, "bootargs", fw_getcmdline());
+       if (err) {
+               pr_err("Unable to set bootargs property: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static __init int append_memory(void *fdt)
+{
+       unsigned long phys_memsize, memsize;
+       __be32 mem_array[2];
+       int err, mem_off;
+       char *var;
+
+       /* find memory size from the bootloader environment */
+       var = fw_getenv("memsize");
+       if (var) {
+               err = kstrtoul(var, 0, &phys_memsize);
+               if (err) {
+                       pr_err("Failed to read memsize env variable '%s'\n",
+                              var);
+                       return -EINVAL;
+               }
+       } else {
+               pr_warn("The bootloader didn't provide memsize: defaulting to 32MB\n");
+               phys_memsize = 32 << 20;
+       }
+
+       /* default to using all available RAM */
+       memsize = phys_memsize;
+
+       /* allow the user to override the usable memory */
+       var = strstr(arcs_cmdline, "memsize=");
+       if (var)
+               memsize = memparse(var + strlen("memsize="), NULL);
+
+       /* if the user says there's more RAM than we thought, believe them */
+       phys_memsize = max_t(unsigned long, phys_memsize, memsize);
+
+       /* find or add a memory node */
+       mem_off = fdt_path_offset(fdt, "/memory");
+       if (mem_off == -FDT_ERR_NOTFOUND)
+               mem_off = fdt_add_subnode(fdt, 0, "memory");
+       if (mem_off < 0) {
+               pr_err("Unable to find or add memory DT node: %d\n", mem_off);
+               return mem_off;
+       }
+
+       err = fdt_setprop_string(fdt, mem_off, "device_type", "memory");
+       if (err) {
+               pr_err("Unable to set memory node device_type: %d\n", err);
+               return err;
+       }
+
+       mem_array[0] = 0;
+       mem_array[1] = cpu_to_be32(phys_memsize);
+       err = fdt_setprop(fdt, mem_off, "reg", mem_array, sizeof(mem_array));
+       if (err) {
+               pr_err("Unable to set memory regs property: %d\n", err);
+               return err;
+       }
+
+       mem_array[0] = 0;
+       mem_array[1] = cpu_to_be32(memsize);
+       err = fdt_setprop(fdt, mem_off, "linux,usable-memory",
+                         mem_array, sizeof(mem_array));
+       if (err) {
+               pr_err("Unable to set linux,usable-memory property: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static __init int remove_gic(void *fdt)
+{
+       const unsigned int cpu_ehci_int = 2;
+       const unsigned int cpu_uart_int = 4;
+       const unsigned int cpu_eth_int = 6;
+       int gic_off, cpu_off, uart_off, eth_off, ehci_off, err;
+       uint32_t cfg, cpu_phandle;
+
+       /* leave the GIC node intact if a GIC is present */
+       cfg = __raw_readl((uint32_t *)SEAD_CONFIG);
+       if (cfg & SEAD_CONFIG_GIC_PRESENT)
+               return 0;
+
+       gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic");
+       if (gic_off < 0) {
+               pr_err("unable to find DT GIC node: %d\n", gic_off);
+               return gic_off;
+       }
+
+       err = fdt_nop_node(fdt, gic_off);
+       if (err) {
+               pr_err("unable to nop GIC node\n");
+               return err;
+       }
+
+       cpu_off = fdt_node_offset_by_compatible(fdt, -1,
+                       "mti,cpu-interrupt-controller");
+       if (cpu_off < 0) {
+               pr_err("unable to find CPU intc node: %d\n", cpu_off);
+               return cpu_off;
+       }
+
+       cpu_phandle = fdt_get_phandle(fdt, cpu_off);
+       if (!cpu_phandle) {
+               pr_err("unable to get CPU intc phandle\n");
+               return -EINVAL;
+       }
+
+       err = fdt_setprop_u32(fdt, 0, "interrupt-parent", cpu_phandle);
+       if (err) {
+               pr_err("unable to set root interrupt-parent: %d\n", err);
+               return err;
+       }
+
+       uart_off = fdt_node_offset_by_compatible(fdt, -1, "ns16550a");
+       while (uart_off >= 0) {
+               err = fdt_setprop_u32(fdt, uart_off, "interrupts",
+                                     cpu_uart_int);
+               if (err) {
+                       pr_err("unable to set UART interrupts property: %d\n",
+                              err);
+                       return err;
+               }
+
+               uart_off = fdt_node_offset_by_compatible(fdt, uart_off,
+                                                        "ns16550a");
+       }
+       if (uart_off != -FDT_ERR_NOTFOUND) {
+               pr_err("error searching for UART DT node: %d\n", uart_off);
+               return uart_off;
+       }
+
+       eth_off = fdt_node_offset_by_compatible(fdt, -1, "smsc,lan9115");
+       if (eth_off < 0) {
+               pr_err("unable to find ethernet DT node: %d\n", eth_off);
+               return eth_off;
+       }
+
+       err = fdt_setprop_u32(fdt, eth_off, "interrupts", cpu_eth_int);
+       if (err) {
+               pr_err("unable to set ethernet interrupts property: %d\n", err);
+               return err;
+       }
+
+       ehci_off = fdt_node_offset_by_compatible(fdt, -1, "generic-ehci");
+       if (ehci_off < 0) {
+               pr_err("unable to find EHCI DT node: %d\n", ehci_off);
+               return ehci_off;
+       }
+
+       err = fdt_setprop_u32(fdt, ehci_off, "interrupts", cpu_ehci_int);
+       if (err) {
+               pr_err("unable to set EHCI interrupts property: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static __init int serial_config(void *fdt)
+{
+       const char *yamontty, *mode_var;
+       char mode_var_name[9], path[18], parity;
+       unsigned int uart, baud, stop_bits;
+       bool hw_flow;
+       int chosen_off, err;
+
+       yamontty = fw_getenv("yamontty");
+       if (!yamontty || !strcmp(yamontty, "tty0")) {
+               uart = 0;
+       } else if (!strcmp(yamontty, "tty1")) {
+               uart = 1;
+       } else {
+               pr_warn("yamontty environment variable '%s' invalid\n",
+                       yamontty);
+               uart = 0;
+       }
+
+       baud = stop_bits = 0;
+       parity = 0;
+       hw_flow = false;
+
+       snprintf(mode_var_name, sizeof(mode_var_name), "modetty%u", uart);
+       mode_var = fw_getenv(mode_var_name);
+       if (mode_var) {
+               while (mode_var[0] >= '0' && mode_var[0] <= '9') {
+                       baud *= 10;
+                       baud += mode_var[0] - '0';
+                       mode_var++;
+               }
+               if (mode_var[0] == ',')
+                       mode_var++;
+               if (mode_var[0])
+                       parity = mode_var[0];
+               if (mode_var[0] == ',')
+                       mode_var++;
+               if (mode_var[0])
+                       stop_bits = mode_var[0] - '0';
+               if (mode_var[0] == ',')
+                       mode_var++;
+               if (!strcmp(mode_var, "hw"))
+                       hw_flow = true;
+       }
+
+       if (!baud)
+               baud = 38400;
+
+       if (parity != 'e' && parity != 'n' && parity != 'o')
+               parity = 'n';
+
+       if (stop_bits != 7 && stop_bits != 8)
+               stop_bits = 8;
+
+       WARN_ON(snprintf(path, sizeof(path), "uart%u:%u%c%u%s",
+                        uart, baud, parity, stop_bits,
+                        hw_flow ? "r" : "") >= sizeof(path));
+
+       /* find or add chosen node */
+       chosen_off = fdt_path_offset(fdt, "/chosen");
+       if (chosen_off == -FDT_ERR_NOTFOUND)
+               chosen_off = fdt_path_offset(fdt, "/chosen@0");
+       if (chosen_off == -FDT_ERR_NOTFOUND)
+               chosen_off = fdt_add_subnode(fdt, 0, "chosen");
+       if (chosen_off < 0) {
+               pr_err("Unable to find or add DT chosen node: %d\n",
+                      chosen_off);
+               return chosen_off;
+       }
+
+       err = fdt_setprop_string(fdt, chosen_off, "stdout-path", path);
+       if (err) {
+               pr_err("Unable to set stdout-path property: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static __init const void *sead3_fixup_fdt(const void *fdt,
+                                         const void *match_data)
+{
+       static unsigned char fdt_buf[16 << 10] __initdata;
+       int err;
+
+       if (fdt_check_header(fdt))
+               panic("Corrupt DT");
+
+       /* if this isn't SEAD3, something went wrong */
+       BUG_ON(fdt_node_check_compatible(fdt, 0, "mti,sead-3"));
+
+       fw_init_cmdline();
+
+       err = fdt_open_into(fdt, fdt_buf, sizeof(fdt_buf));
+       if (err)
+               panic("Unable to open FDT: %d", err);
+
+       err = append_cmdline(fdt_buf);
+       if (err)
+               panic("Unable to patch FDT: %d", err);
+
+       err = append_memory(fdt_buf);
+       if (err)
+               panic("Unable to patch FDT: %d", err);
+
+       err = remove_gic(fdt_buf);
+       if (err)
+               panic("Unable to patch FDT: %d", err);
+
+       err = serial_config(fdt_buf);
+       if (err)
+               panic("Unable to patch FDT: %d", err);
+
+       err = fdt_pack(fdt_buf);
+       if (err)
+               panic("Unable to pack FDT: %d\n", err);
+
+       return fdt_buf;
+}
+
+static __init unsigned int sead3_measure_hpt_freq(void)
+{
+       void __iomem *status_reg = (void __iomem *)0xbf000410;
+       unsigned int freq, orig, tick = 0;
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       orig = readl(status_reg) & 0x2;               /* get original sample */
+       /* wait for transition */
+       while ((readl(status_reg) & 0x2) == orig)
+               ;
+       orig = orig ^ 0x2;                            /* flip the bit */
+
+       write_c0_count(0);
+
+       /* wait 1 second (the sampling clock transitions every 10ms) */
+       while (tick < 100) {
+               /* wait for transition */
+               while ((readl(status_reg) & 0x2) == orig)
+                       ;
+               orig = orig ^ 0x2;                            /* flip the bit */
+               tick++;
+       }
+
+       freq = read_c0_count();
+
+       local_irq_restore(flags);
+
+       return freq;
+}
+
+extern char __dtb_sead3_begin[];
+
+MIPS_MACHINE(sead3) = {
+       .fdt = __dtb_sead3_begin,
+       .detect = sead3_detect,
+       .fixup_fdt = sead3_fixup_fdt,
+       .measure_hpt_freq = sead3_measure_hpt_freq,
+};
diff --git a/arch/mips/generic/init.c b/arch/mips/generic/init.c
new file mode 100644 (file)
index 0000000..0ea73e8
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <linux/irqchip.h>
+#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
+
+#include <asm/fw/fw.h>
+#include <asm/irq_cpu.h>
+#include <asm/machine.h>
+#include <asm/mips-cpc.h>
+#include <asm/prom.h>
+#include <asm/smp-ops.h>
+#include <asm/time.h>
+
+static __initdata const void *fdt;
+static __initdata const struct mips_machine *mach;
+static __initdata const void *mach_match_data;
+
+void __init prom_init(void)
+{
+       const struct mips_machine *check_mach;
+       const struct of_device_id *match;
+
+       if ((fw_arg0 == -2) && !fdt_check_header((void *)fw_arg1)) {
+               /*
+                * We booted using the UHI boot protocol, so we have been
+                * provided with the appropriate device tree for the board.
+                * Make use of it & search for any machine struct based upon
+                * the root compatible string.
+                */
+               fdt = (void *)fw_arg1;
+
+               for_each_mips_machine(check_mach) {
+                       match = mips_machine_is_compatible(check_mach, fdt);
+                       if (match) {
+                               mach = check_mach;
+                               mach_match_data = match->data;
+                               break;
+                       }
+               }
+       } else if (IS_ENABLED(CONFIG_LEGACY_BOARDS)) {
+               /*
+                * We weren't booted using the UHI boot protocol, but do
+                * support some number of boards with legacy boot protocols.
+                * Attempt to find the right one.
+                */
+               for_each_mips_machine(check_mach) {
+                       if (!check_mach->detect)
+                               continue;
+
+                       if (!check_mach->detect())
+                               continue;
+
+                       mach = check_mach;
+               }
+
+               /*
+                * If we don't recognise the machine then we can't continue, so
+                * die here.
+                */
+               BUG_ON(!mach);
+
+               /* Retrieve the machine's FDT */
+               fdt = mach->fdt;
+       }
+
+       BUG_ON(!fdt);
+}
+
+void __init *plat_get_fdt(void)
+{
+       return (void *)fdt;
+}
+
+void __init plat_mem_setup(void)
+{
+       if (mach && mach->fixup_fdt)
+               fdt = mach->fixup_fdt(fdt, mach_match_data);
+
+       strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
+       __dt_setup_arch((void *)fdt);
+}
+
+void __init device_tree_init(void)
+{
+       int err;
+
+       unflatten_and_copy_device_tree();
+       mips_cpc_probe();
+
+       err = register_cps_smp_ops();
+       if (err)
+               err = register_up_smp_ops();
+}
+
+void __init plat_time_init(void)
+{
+       struct device_node *np;
+       struct clk *clk;
+
+       of_clk_init(NULL);
+
+       if (!cpu_has_counter) {
+               mips_hpt_frequency = 0;
+       } else if (mach && mach->measure_hpt_freq) {
+               mips_hpt_frequency = mach->measure_hpt_freq();
+       } else {
+               np = of_get_cpu_node(0, NULL);
+               if (!np) {
+                       pr_err("Failed to get CPU node\n");
+                       return;
+               }
+
+               clk = of_clk_get(np, 0);
+               if (IS_ERR(clk)) {
+                       pr_err("Failed to get CPU clock: %ld\n", PTR_ERR(clk));
+                       return;
+               }
+
+               mips_hpt_frequency = clk_get_rate(clk);
+               clk_put(clk);
+
+               switch (boot_cpu_type()) {
+               case CPU_20KC:
+               case CPU_25KF:
+                       /* The counter runs at the CPU clock rate */
+                       break;
+               default:
+                       /* The counter runs at half the CPU clock rate */
+                       mips_hpt_frequency /= 2;
+                       break;
+               }
+       }
+
+       clocksource_probe();
+}
+
+void __init arch_init_irq(void)
+{
+       struct device_node *intc_node;
+
+       intc_node = of_find_compatible_node(NULL, NULL,
+                                           "mti,cpu-interrupt-controller");
+       if (!cpu_has_veic && !intc_node)
+               mips_cpu_irq_init();
+
+       irqchip_init();
+}
+
+static int __init publish_devices(void)
+{
+       if (!of_have_populated_dt())
+               panic("Device-tree not present");
+
+       if (of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL))
+               panic("Failed to populate DT");
+
+       return 0;
+}
+arch_initcall(publish_devices);
+
+void __init prom_free_prom_memory(void)
+{
+}
diff --git a/arch/mips/generic/irq.c b/arch/mips/generic/irq.c
new file mode 100644 (file)
index 0000000..14064bd
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <linux/irqchip/mips-gic.h>
+#include <linux/types.h>
+
+#include <asm/irq.h>
+
+int get_c0_fdc_int(void)
+{
+       int mips_cpu_fdc_irq;
+
+       if (cpu_has_veic)
+               panic("Unimplemented!");
+       else if (gic_present)
+               mips_cpu_fdc_irq = gic_get_c0_fdc_int();
+       else if (cp0_fdc_irq >= 0)
+               mips_cpu_fdc_irq = MIPS_CPU_IRQ_BASE + cp0_fdc_irq;
+       else
+               mips_cpu_fdc_irq = -1;
+
+       return mips_cpu_fdc_irq;
+}
+
+int get_c0_perfcount_int(void)
+{
+       int mips_cpu_perf_irq;
+
+       if (cpu_has_veic)
+               panic("Unimplemented!");
+       else if (gic_present)
+               mips_cpu_perf_irq = gic_get_c0_perfcount_int();
+       else if (cp0_perfcount_irq >= 0)
+               mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
+       else
+               mips_cpu_perf_irq = -1;
+
+       return mips_cpu_perf_irq;
+}
+
+unsigned int get_c0_compare_int(void)
+{
+       int mips_cpu_timer_irq;
+
+       if (cpu_has_veic)
+               panic("Unimplemented!");
+       else if (gic_present)
+               mips_cpu_timer_irq = gic_get_c0_compare_int();
+       else
+               mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
+
+       return mips_cpu_timer_irq;
+}
diff --git a/arch/mips/generic/proc.c b/arch/mips/generic/proc.c
new file mode 100644 (file)
index 0000000..42b3325
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.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/of.h>
+
+#include <asm/bootinfo.h>
+
+const char *get_system_type(void)
+{
+       const char *str;
+       int err;
+
+       err = of_property_read_string(of_root, "model", &str);
+       if (!err)
+               return str;
+
+       err = of_property_read_string_index(of_root, "compatible", 0, &str);
+       if (!err)
+               return str;
+
+       return "Unknown";
+}
diff --git a/arch/mips/generic/vmlinux.its.S b/arch/mips/generic/vmlinux.its.S
new file mode 100644 (file)
index 0000000..f67fbf1
--- /dev/null
@@ -0,0 +1,31 @@
+/dts-v1/;
+
+/ {
+       description = KERNEL_NAME;
+       #address-cells = <ADDR_CELLS>;
+
+       images {
+               kernel@0 {
+                       description = KERNEL_NAME;
+                       data = /incbin/(VMLINUX_BINARY);
+                       type = "kernel";
+                       arch = "mips";
+                       os = "linux";
+                       compression = VMLINUX_COMPRESSION;
+                       load = /bits/ ADDR_BITS <VMLINUX_LOAD_ADDRESS>;
+                       entry = /bits/ ADDR_BITS <VMLINUX_ENTRY_ADDRESS>;
+                       hash@0 {
+                               algo = "sha1";
+                       };
+               };
+       };
+
+       configurations {
+               default = "conf@default";
+
+               conf@default {
+                       description = "Generic Linux kernel";
+                       kernel = "kernel@0";
+               };
+       };
+};
index c5b04e752e9762f4920d3b305356d9778216ee65..4856adc8906ef3a7c337fe1a14c7e08af426e29e 100644 (file)
 #define PHYS_TO_XKSEG_UNCACHED(p)      PHYS_TO_XKPHYS(K_CALG_UNCACHED, (p))
 #define PHYS_TO_XKSEG_CACHED(p)                PHYS_TO_XKPHYS(K_CALG_COH_SHAREABLE, (p))
 #define XKPHYS_TO_PHYS(p)              ((p) & TO_PHYS_MASK)
-#define PHYS_TO_XKPHYS(cm, a)          (_CONST64_(0x8000000000000000) | \
-                                        (_CONST64_(cm) << 59) | (a))
+#define PHYS_TO_XKPHYS(cm, a)          (XKPHYS | (_ACAST64_(cm) << 59) | (a))
 
 /*
  * The ultimate limited of the 64-bit MIPS architecture:  2 bits for selecting
index d296633d890e56c892059871499e989c85a93203..a5eb1bb199a7fdf76087bcea1d63b962e2555606 100644 (file)
 
 #include <asm/addrspace.h>
 
+/*
+ * Sync types defined by the MIPS architecture (document MD00087 table 6.5)
+ * These values are used with the sync instruction to perform memory barriers.
+ * Types of ordering guarantees available through the SYNC instruction:
+ * - Completion Barriers
+ * - Ordering Barriers
+ * As compared to the completion barrier, the ordering barrier is a
+ * lighter-weight operation as it does not require the specified instructions
+ * before the SYNC to be already completed. Instead it only requires that those
+ * specified instructions which are subsequent to the SYNC in the instruction
+ * stream are never re-ordered for processing ahead of the specified
+ * instructions which are before the SYNC in the instruction stream.
+ * This potentially reduces how many cycles the barrier instruction must stall
+ * before it completes.
+ * Implementations that do not use any of the non-zero values of stype to define
+ * different barriers, such as ordering barriers, must make those stype values
+ * act the same as stype zero.
+ */
+
+/*
+ * Completion barriers:
+ * - Every synchronizable specified memory instruction (loads or stores or both)
+ *   that occurs in the instruction stream before the SYNC instruction must be
+ *   already globally performed before any synchronizable specified memory
+ *   instructions that occur after the SYNC are allowed to be performed, with
+ *   respect to any other processor or coherent I/O module.
+ *
+ * - The barrier does not guarantee the order in which instruction fetches are
+ *   performed.
+ *
+ * - A stype value of zero will always be defined such that it performs the most
+ *   complete set of synchronization operations that are defined.This means
+ *   stype zero always does a completion barrier that affects both loads and
+ *   stores preceding the SYNC instruction and both loads and stores that are
+ *   subsequent to the SYNC instruction. Non-zero values of stype may be defined
+ *   by the architecture or specific implementations to perform synchronization
+ *   behaviors that are less complete than that of stype zero. If an
+ *   implementation does not use one of these non-zero values to define a
+ *   different synchronization behavior, then that non-zero value of stype must
+ *   act the same as stype zero completion barrier. This allows software written
+ *   for an implementation with a lighter-weight barrier to work on another
+ *   implementation which only implements the stype zero completion barrier.
+ *
+ * - A completion barrier is required, potentially in conjunction with SSNOP (in
+ *   Release 1 of the Architecture) or EHB (in Release 2 of the Architecture),
+ *   to guarantee that memory reference results are visible across operating
+ *   mode changes. For example, a completion barrier is required on some
+ *   implementations on entry to and exit from Debug Mode to guarantee that
+ *   memory effects are handled correctly.
+ */
+
+/*
+ * stype 0 - A completion barrier that affects preceding loads and stores and
+ * subsequent loads and stores.
+ * Older instructions which must reach the load/store ordering point before the
+ * SYNC instruction completes: Loads, Stores
+ * Younger instructions which must reach the load/store ordering point only
+ * after the SYNC instruction completes: Loads, Stores
+ * Older instructions which must be globally performed when the SYNC instruction
+ * completes: Loads, Stores
+ */
+#define STYPE_SYNC 0x0
+
+/*
+ * Ordering barriers:
+ * - Every synchronizable specified memory instruction (loads or stores or both)
+ *   that occurs in the instruction stream before the SYNC instruction must
+ *   reach a stage in the load/store datapath after which no instruction
+ *   re-ordering is possible before any synchronizable specified memory
+ *   instruction which occurs after the SYNC instruction in the instruction
+ *   stream reaches the same stage in the load/store datapath.
+ *
+ * - If any memory instruction before the SYNC instruction in program order,
+ *   generates a memory request to the external memory and any memory
+ *   instruction after the SYNC instruction in program order also generates a
+ *   memory request to external memory, the memory request belonging to the
+ *   older instruction must be globally performed before the time the memory
+ *   request belonging to the younger instruction is globally performed.
+ *
+ * - The barrier does not guarantee the order in which instruction fetches are
+ *   performed.
+ */
+
+/*
+ * stype 0x10 - An ordering barrier that affects preceding loads and stores and
+ * subsequent loads and stores.
+ * Older instructions which must reach the load/store ordering point before the
+ * SYNC instruction completes: Loads, Stores
+ * Younger instructions which must reach the load/store ordering point only
+ * after the SYNC instruction completes: Loads, Stores
+ * Older instructions which must be globally performed when the SYNC instruction
+ * completes: N/A
+ */
+#define STYPE_SYNC_MB 0x10
+
+
 #ifdef CONFIG_CPU_HAS_SYNC
 #define __sync()                               \
        __asm__ __volatile__(                   \
index 34ed22ec6c33e7917386b31007f6f14e597a1e10..4812d1fed0c2ccf7390830c719daad0546a2b0b3 100644 (file)
@@ -28,6 +28,7 @@
  *  - flush_cache_sigtramp() flush signal trampoline
  *  - flush_icache_all() flush the entire instruction cache
  *  - flush_data_cache_page() flushes a page from the data cache
+ *  - __flush_icache_user_range(start, end) flushes range of user instructions
  */
 
  /*
@@ -80,6 +81,10 @@ static inline void flush_icache_page(struct vm_area_struct *vma,
 
 extern void (*flush_icache_range)(unsigned long start, unsigned long end);
 extern void (*local_flush_icache_range)(unsigned long start, unsigned long end);
+extern void (*__flush_icache_user_range)(unsigned long start,
+                                        unsigned long end);
+extern void (*__local_flush_icache_user_range)(unsigned long start,
+                                              unsigned long end);
 
 extern void (*__flush_cache_vmap)(void);
 
index fbe1881f28fca71d6a4f5e6306357e3c14818e98..bdd6dc18e65c618dc65bd8a487895386c8085eea 100644 (file)
@@ -24,7 +24,8 @@ static inline int __pure __get_cpu_type(const int cpu_type)
        case CPU_LOONGSON3:
 #endif
 
-#ifdef CONFIG_SYS_HAS_CPU_LOONGSON1B
+#if defined(CONFIG_SYS_HAS_CPU_LOONGSON1B) || \
+    defined(CONFIG_SYS_HAS_CPU_LOONGSON1C)
        case CPU_LOONGSON1:
 #endif
 
index f672df8b26d0126c7dd9ffd783fad8102fd1d24c..9a8372484edc0f3dd48daf5e06a532473ff911db 100644 (file)
 #define PRID_REV_VR4130                0x0080
 #define PRID_REV_34K_V1_0_2    0x0022
 #define PRID_REV_LOONGSON1B    0x0020
+#define PRID_REV_LOONGSON1C    0x0020  /* Same as Loongson-1B */
 #define PRID_REV_LOONGSON2E    0x0002
 #define PRID_REV_LOONGSON2F    0x0003
 #define PRID_REV_LOONGSON3A_R1 0x0005
index c94fafba9e62fe238c5554e6910fb08820e83ed3..21c2082a0dfbb3b1dec84125f9216d54accd4edf 100644 (file)
@@ -11,6 +11,11 @@ struct dma_map_ops;
 struct dev_archdata {
        /* DMA operations on that device */
        struct dma_map_ops *dma_ops;
+
+#ifdef CONFIG_DMA_PERDEV_COHERENT
+       /* Non-zero if DMA is coherent with CPU caches */
+       bool dma_coherent;
+#endif
 };
 
 struct pdev_archdata {
index bc5e85d579e607a61420a7c0cd7a1c6b716d8f4c..72d0eab02afcbbfc33bd6ee653389a30887c1f3a 100644 (file)
@@ -9,14 +9,22 @@
 #ifndef __ASM_DMA_COHERENCE_H
 #define __ASM_DMA_COHERENCE_H
 
-#ifdef CONFIG_DMA_MAYBE_COHERENT
-extern int coherentio;
+enum coherent_io_user_state {
+       IO_COHERENCE_DEFAULT,
+       IO_COHERENCE_ENABLED,
+       IO_COHERENCE_DISABLED,
+};
+
+#if defined(CONFIG_DMA_PERDEV_COHERENT)
+/* Don't provide (hw_)coherentio to avoid misuse */
+#elif defined(CONFIG_DMA_MAYBE_COHERENT)
+extern enum coherent_io_user_state coherentio;
 extern int hw_coherentio;
 #else
 #ifdef CONFIG_DMA_COHERENT
-#define coherentio     1
+#define coherentio     IO_COHERENCE_ENABLED
 #else
-#define coherentio     0
+#define coherentio     IO_COHERENCE_DISABLED
 #endif
 #define hw_coherentio  0
 #endif /* CONFIG_DMA_MAYBE_COHERENT */
index 12fa79e2f1b4fc7fe7c66f1f93d344bb7f1d67fb..7aa71b9b0258f1fc349fbc2cc78b1b928ac730f8 100644 (file)
@@ -32,4 +32,14 @@ static inline void dma_mark_clean(void *addr, size_t size) {}
 extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
               enum dma_data_direction direction);
 
+#define arch_setup_dma_ops arch_setup_dma_ops
+static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base,
+                                     u64 size, const struct iommu_ops *iommu,
+                                     bool coherent)
+{
+#ifdef CONFIG_DMA_PERDEV_COHERENT
+       dev->archdata.dma_coherent = coherent;
+#endif
+}
+
 #endif /* _ASM_DMA_MAPPING_H */
index a7fbcd6ed13c4fb41d19609102ea5813feba17c2..32229c77906a1efb8b151bd3f491a452ef2c24d7 100644 (file)
 
 extern raw_spinlock_t i8259A_lock;
 
-extern int i8259A_irq_pending(unsigned int irq);
 extern void make_8259A_irq(unsigned int irq);
 
 extern void init_i8259_irqs(void);
 extern int i8259_of_init(struct device_node *node, struct device_node *parent);
 
+/**
+ * i8159_set_poll() - Override the i8259 polling function
+ * @poll: pointer to platform-specific polling function
+ *
+ * Call this to override the generic i8259 polling function, which directly
+ * accesses i8259 registers, with a platform specific one which may be faster
+ * in cases where hardware provides a more optimal means of polling for an
+ * interrupt.
+ */
+extern void i8259_set_poll(int (*poll)(void));
+
 /*
  * Do the traditional i8259 interrupt polling thing.  This is for the few
  * cases where no better interrupt acknowledge method is available and we
index 0f8a354fd4686dc49cbd6718ef565e9b73786386..61addb1677e950c75936cce24905f850e23e7a04 100644 (file)
@@ -49,7 +49,19 @@ static inline int plat_dma_supported(struct device *dev, u64 mask)
 
 static inline int plat_device_is_coherent(struct device *dev)
 {
-       return coherentio;
+#ifdef CONFIG_DMA_PERDEV_COHERENT
+       return dev->archdata.dma_coherent;
+#else
+       switch (coherentio) {
+       default:
+       case IO_COHERENCE_DEFAULT:
+               return hw_coherentio;
+       case IO_COHERENCE_ENABLED:
+               return 1;
+       case IO_COHERENCE_DISABLED:
+               return 0;
+       }
+#endif
 }
 
 #ifndef plat_post_dma_flush
index e2561d99a3feaf12d9d54d948cf2403fc18a8d5c..9ec2f6a5200b6f7ce5bff8d2708b1e7849972d3a 100644 (file)
@@ -115,11 +115,7 @@ static inline unsigned long fd_getfdaddr1(void)
 
 static inline unsigned long fd_dma_mem_alloc(unsigned long size)
 {
-       unsigned long mem;
-
-       mem = __get_dma_pages(GFP_KERNEL, get_order(size));
-
-       return mem;
+       return __get_dma_pages(GFP_KERNEL, get_order(size));
 }
 
 static inline void fd_dma_mem_free(unsigned long addr, unsigned long size)
index afc96ecb90042358685be12d3b2e0f8ab21964ce..952b0fdfda0e637849315f534043137f10972414 100644 (file)
@@ -12,6 +12,8 @@
 
 #include <linux/const.h>
 
+#include <asm/mipsregs.h>
+
 /*
  * This gives the physical RAM offset.
  */
 #ifdef CONFIG_64BIT
 
 #ifndef CAC_BASE
-#ifdef CONFIG_DMA_NONCOHERENT
-#define CAC_BASE               _AC(0x9800000000000000, UL)
-#else
-#define CAC_BASE               _AC(0xa800000000000000, UL)
-#endif
+#define CAC_BASE       PHYS_TO_XKPHYS(read_c0_config() & CONF_CM_CMASK, 0)
 #endif
 
 #ifndef IO_BASE
index b18802a0b17e94dca9a85cc6a8a3c761dfb9f60e..4775a1136a5b45eea2359b74bfe530320e95d9df 100644 (file)
@@ -19,6 +19,7 @@
 #define IO_BASE                        0x9200000000000000
 #define MSPEC_BASE             0x9400000000000000
 #define UNCAC_BASE             0x9600000000000000
+#define CAC_BASE               0xa800000000000000
 
 #define TO_MSPEC(x)            (MSPEC_BASE | ((x) & TO_PHYS_MASK))
 #define TO_HSPEC(x)            (HSPEC_BASE | ((x) & TO_PHYS_MASK))
index c1c744197de498e4c38be02b18f4732f789ae4ea..8c01b304b7ec89464088ed55b36df1dca4f1ed71 100644 (file)
 #define LS1X_IRQ(n, x)                 (LS1X_IRQ_BASE + (n << 5) + (x))
 
 #define LS1X_UART0_IRQ                 LS1X_IRQ(0, 2)
+#if defined(CONFIG_LOONGSON1_LS1B)
 #define LS1X_UART1_IRQ                 LS1X_IRQ(0, 3)
 #define LS1X_UART2_IRQ                 LS1X_IRQ(0, 4)
 #define LS1X_UART3_IRQ                 LS1X_IRQ(0, 5)
+#elif defined(CONFIG_LOONGSON1_LS1C)
+#define LS1X_UART1_IRQ                 LS1X_IRQ(0, 4)
+#define LS1X_UART2_IRQ                 LS1X_IRQ(0, 5)
+#endif
 #define LS1X_CAN0_IRQ                  LS1X_IRQ(0, 6)
 #define LS1X_CAN1_IRQ                  LS1X_IRQ(0, 7)
 #define LS1X_SPI0_IRQ                  LS1X_IRQ(0, 8)
@@ -47,6 +52,9 @@
 #define LS1X_DMA0_IRQ                  LS1X_IRQ(0, 13)
 #define LS1X_DMA1_IRQ                  LS1X_IRQ(0, 14)
 #define LS1X_DMA2_IRQ                  LS1X_IRQ(0, 15)
+#if defined(CONFIG_LOONGSON1_LS1C)
+#define LS1X_NAND_IRQ                  LS1X_IRQ(0, 16)
+#endif
 #define LS1X_PWM0_IRQ                  LS1X_IRQ(0, 17)
 #define LS1X_PWM1_IRQ                  LS1X_IRQ(0, 18)
 #define LS1X_PWM2_IRQ                  LS1X_IRQ(0, 19)
 #define LS1X_RTC_INT0_IRQ              LS1X_IRQ(0, 21)
 #define LS1X_RTC_INT1_IRQ              LS1X_IRQ(0, 22)
 #define LS1X_RTC_INT2_IRQ              LS1X_IRQ(0, 23)
+#if defined(CONFIG_LOONGSON1_LS1B)
 #define LS1X_TOY_INT0_IRQ              LS1X_IRQ(0, 24)
 #define LS1X_TOY_INT1_IRQ              LS1X_IRQ(0, 25)
 #define LS1X_TOY_INT2_IRQ              LS1X_IRQ(0, 26)
 #define LS1X_RTC_TICK_IRQ              LS1X_IRQ(0, 27)
 #define LS1X_TOY_TICK_IRQ              LS1X_IRQ(0, 28)
+#define LS1X_UART4_IRQ                 LS1X_IRQ(0, 29)
+#define LS1X_UART5_IRQ                 LS1X_IRQ(0, 30)
+#elif defined(CONFIG_LOONGSON1_LS1C)
+#define LS1X_UART3_IRQ                 LS1X_IRQ(0, 29)
+#define LS1X_ADC_IRQ                   LS1X_IRQ(0, 30)
+#define LS1X_SDIO_IRQ                  LS1X_IRQ(0, 31)
+#endif
 
 #define LS1X_EHCI_IRQ                  LS1X_IRQ(1, 0)
 #define LS1X_OHCI_IRQ                  LS1X_IRQ(1, 1)
+#if defined(CONFIG_LOONGSON1_LS1B)
 #define LS1X_GMAC0_IRQ                 LS1X_IRQ(1, 2)
 #define LS1X_GMAC1_IRQ                 LS1X_IRQ(1, 3)
+#elif defined(CONFIG_LOONGSON1_LS1C)
+#define LS1X_OTG_IRQ                   LS1X_IRQ(1, 2)
+#define LS1X_GMAC0_IRQ                 LS1X_IRQ(1, 3)
+#define LS1X_CAM_IRQ                   LS1X_IRQ(1, 4)
+#define LS1X_UART4_IRQ                 LS1X_IRQ(1, 5)
+#define LS1X_UART5_IRQ                 LS1X_IRQ(1, 6)
+#define LS1X_UART6_IRQ                 LS1X_IRQ(1, 7)
+#define LS1X_UART7_IRQ                 LS1X_IRQ(1, 8)
+#define LS1X_UART8_IRQ                 LS1X_IRQ(1, 9)
+#define LS1X_UART9_IRQ                 LS1X_IRQ(1, 13)
+#define LS1X_UART10_IRQ                        LS1X_IRQ(1, 14)
+#define LS1X_UART11_IRQ                        LS1X_IRQ(1, 15)
+#define LS1X_I2C0_IRQ                  LS1X_IRQ(1, 17)
+#define LS1X_I2C1_IRQ                  LS1X_IRQ(1, 18)
+#define LS1X_I2C2_IRQ                  LS1X_IRQ(1, 19)
+#endif
 
-#define LS1X_IRQS              (LS1X_IRQ(4, 31) + 1 - LS1X_IRQ_BASE)
+#if defined(CONFIG_LOONGSON1_LS1B)
+#define INTN   4
+#elif defined(CONFIG_LOONGSON1_LS1C)
+#define INTN   5
+#endif
+
+#define LS1X_IRQS              (LS1X_IRQ(INTN, 31) + 1 - LS1X_IRQ_BASE)
 
 #define NR_IRQS                        (MIPS_CPU_IRQS + LS1X_IRQS)
 
index 978f6df8970a38946ddd5afcdb21d0ba5f3f9312..3584c40caf796d1995d3b389b3a4aab2fd612a18 100644 (file)
 #ifndef __ASM_MACH_LOONGSON32_LOONGSON1_H
 #define __ASM_MACH_LOONGSON32_LOONGSON1_H
 
+#if defined(CONFIG_LOONGSON1_LS1B)
 #define DEFAULT_MEMSIZE                        256     /* If no memsize provided */
+#elif defined(CONFIG_LOONGSON1_LS1C)
+#define DEFAULT_MEMSIZE                        32
+#endif
 
 /* Loongson 1 Register Bases */
 #define LS1X_MUX_BASE                  0x1fd00420
@@ -20,6 +24,7 @@
 #define LS1X_GPIO0_BASE                        0x1fd010c0
 #define LS1X_GPIO1_BASE                        0x1fd010c4
 #define LS1X_DMAC_BASE                 0x1fd01160
+#define LS1X_CBUS_BASE                 0x1fd011c0
 #define LS1X_EHCI_BASE                 0x1fe00000
 #define LS1X_OHCI_BASE                 0x1fe08000
 #define LS1X_GMAC0_BASE                        0x1fe10000
index 672531aa9bef0a20488f578682e27ae81d113d61..7adc313649395265c168178528d928d16b7483ac 100644 (file)
@@ -30,5 +30,6 @@ void __init ls1x_clk_init(void);
 void __init ls1x_dma_set_platdata(struct plat_ls1x_dma *pdata);
 void __init ls1x_nand_set_platdata(struct plat_ls1x_nand *pdata);
 void __init ls1x_serial_set_uartclk(struct platform_device *pdev);
+void __init ls1x_rtc_set_extclk(struct platform_device *pdev);
 
 #endif /* __ASM_MACH_LOONGSON32_PLATFORM_H */
index 4d56fc38f0c47be5604a6688754528d43c803240..e5e8f118f34b209f2d7d9e389f710e24166cbdac 100644 (file)
@@ -18,6 +18,7 @@
 #define LS1X_CLK_PLL_FREQ              LS1X_CLK_REG(0x0)
 #define LS1X_CLK_PLL_DIV               LS1X_CLK_REG(0x4)
 
+#if defined(CONFIG_LOONGSON1_LS1B)
 /* Clock PLL Divisor Register Bits */
 #define DIV_DC_EN                      BIT(31)
 #define DIV_DC_RST                     BIT(30)
 #define BYPASS_DDR_WIDTH               1
 #define BYPASS_CPU_WIDTH               1
 
+#elif defined(CONFIG_LOONGSON1_LS1C)
+/* PLL/SDRAM Frequency configuration register Bits */
+#define PLL_VALID                      BIT(31)
+#define FRAC_N                         GENMASK(23, 16)
+#define RST_TIME                       GENMASK(3, 2)
+#define SDRAM_DIV                      GENMASK(1, 0)
+
+/* CPU/CAMERA/DC Frequency configuration register Bits */
+#define DIV_DC_EN                      BIT(31)
+#define DIV_DC                         GENMASK(30, 24)
+#define DIV_CAM_EN                     BIT(23)
+#define DIV_CAM                                GENMASK(22, 16)
+#define DIV_CPU_EN                     BIT(15)
+#define DIV_CPU                                GENMASK(14, 8)
+#define DIV_DC_SEL_EN                  BIT(5)
+#define DIV_DC_SEL                     BIT(4)
+#define DIV_CAM_SEL_EN                 BIT(3)
+#define DIV_CAM_SEL                    BIT(2)
+#define DIV_CPU_SEL_EN                 BIT(1)
+#define DIV_CPU_SEL                    BIT(0)
+
+#define DIV_DC_SHIFT                   24
+#define DIV_CAM_SHIFT                  16
+#define DIV_CPU_SHIFT                  8
+#define DIV_DDR_SHIFT                  0
+
+#define DIV_DC_WIDTH                   7
+#define DIV_CAM_WIDTH                  7
+#define DIV_CPU_WIDTH                  7
+#define DIV_DDR_WIDTH                  2
+
+#endif
+
 #endif /* __ASM_MACH_LOONGSON32_REGS_CLK_H */
index 7c394f93cb9e381adb5b1a813cc1745383e80532..4a0bdeb0eb9b94ae810dd76593a1988224d6cf23 100644 (file)
@@ -18,6 +18,7 @@
 #define LS1X_MUX_CTRL0                 LS1X_MUX_REG(0x0)
 #define LS1X_MUX_CTRL1                 LS1X_MUX_REG(0x4)
 
+#if defined(CONFIG_LOONGSON1_LS1B)
 /* MUX CTRL0 Register Bits */
 #define UART0_USE_PWM23                        BIT(28)
 #define UART0_USE_PWM01                        BIT(27)
 #define GMAC1_USE_PWM23                        BIT(1)
 #define GMAC0_USE_PWM01                        BIT(0)
 
+#elif defined(CONFIG_LOONGSON1_LS1C)
+
+/* SHUT_CTRL Register Bits */
+#define UART_SPLIT                     GENMASK(31, 30)
+#define OUTPUT_CLK                     GENMASK(29, 26)
+#define ADC_SHUT                       BIT(25)
+#define SDIO_SHUT                      BIT(24)
+#define DMA2_SHUT                      BIT(23)
+#define DMA1_SHUT                      BIT(22)
+#define DMA0_SHUT                      BIT(21)
+#define SPI1_SHUT                      BIT(20)
+#define SPI0_SHUT                      BIT(19)
+#define I2C2_SHUT                      BIT(18)
+#define I2C1_SHUT                      BIT(17)
+#define I2C0_SHUT                      BIT(16)
+#define AC97_SHUT                      BIT(15)
+#define I2S_SHUT                       BIT(14)
+#define UART3_SHUT                     BIT(13)
+#define UART2_SHUT                     BIT(12)
+#define UART1_SHUT                     BIT(11)
+#define UART0_SHUT                     BIT(10)
+#define CAN1_SHUT                      BIT(9)
+#define CAN0_SHUT                      BIT(8)
+#define ECC_SHUT                       BIT(7)
+#define GMAC_SHUT                      BIT(6)
+#define USBHOST_SHUT                   BIT(5)
+#define USBOTG_SHUT                    BIT(4)
+#define SDRAM_SHUT                     BIT(3)
+#define SRAM_SHUT                      BIT(2)
+#define CAM_SHUT                       BIT(1)
+#define LCD_SHUT                       BIT(0)
+
+#define UART_SPLIT_SHIFT                        30
+#define OUTPUT_CLK_SHIFT                        26
+
+/* MISC_CTRL Register Bits */
+#define USBHOST_RSTN                   BIT(31)
+#define PHY_INTF_SELI                  GENMASK(30, 28)
+#define AC97_EN                                BIT(25)
+#define SDIO_DMA_EN                    GENMASK(24, 23)
+#define ADC_DMA_EN                     BIT(22)
+#define SDIO_USE_SPI1                  BIT(17)
+#define SDIO_USE_SPI0                  BIT(16)
+#define SRAM_CTRL                      GENMASK(15, 0)
+
+#define PHY_INTF_SELI_SHIFT                     28
+#define SDIO_DMA_EN_SHIFT                       23
+#define SRAM_CTRL_SHIFT                                0
+
+#define LS1X_CBUS_REG(n, x) \
+               ((void __iomem *)KSEG1ADDR(LS1X_CBUS_BASE + (n * 0x04) + (x)))
+
+#define LS1X_CBUS_FIRST(n)             LS1X_CBUS_REG(n, 0x00)
+#define LS1X_CBUS_SECOND(n)            LS1X_CBUS_REG(n, 0x10)
+#define LS1X_CBUS_THIRD(n)             LS1X_CBUS_REG(n, 0x20)
+#define LS1X_CBUS_FOURTHT(n)           LS1X_CBUS_REG(n, 0x30)
+#define LS1X_CBUS_FIFTHT(n)            LS1X_CBUS_REG(n, 0x40)
+
+#endif
+
 #endif /* __ASM_MACH_LOONGSON32_REGS_MUX_H */
diff --git a/arch/mips/include/asm/mach-sead3/cpu-feature-overrides.h b/arch/mips/include/asm/mach-sead3/cpu-feature-overrides.h
deleted file mode 100644 (file)
index bfbd703..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2003, 2004 Chris Dearman
- * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org)
- */
-#ifndef __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H
-#define __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H
-
-
-/*
- * CPU feature overrides for MIPS boards
- */
-#ifdef CONFIG_CPU_MIPS32
-#define cpu_has_tlb            1
-#define cpu_has_4kex           1
-#define cpu_has_4k_cache       1
-/* #define cpu_has_fpu         ? */
-/* #define cpu_has_32fpr       ? */
-#define cpu_has_counter                1
-/* #define cpu_has_watch       ? */
-#define cpu_has_divec          1
-#define cpu_has_vce            0
-/* #define cpu_has_cache_cdex_p ? */
-/* #define cpu_has_cache_cdex_s ? */
-/* #define cpu_has_prefetch    ? */
-#define cpu_has_mcheck         1
-/* #define cpu_has_ejtag       ? */
-#ifdef CONFIG_CPU_MICROMIPS
-#define cpu_has_llsc           0
-#else
-#define cpu_has_llsc           1
-#endif
-/* #define cpu_has_vtag_icache ? */
-/* #define cpu_has_dc_aliases  ? */
-/* #define cpu_has_ic_fills_f_dc ? */
-#define cpu_has_nofpuex                0
-/* #define cpu_has_64bits      ? */
-/* #define cpu_has_64bit_zero_reg ? */
-/* #define cpu_has_inclusive_pcaches ? */
-#define cpu_icache_snoops_remote_store 1
-#endif
-
-#ifdef CONFIG_CPU_MIPS64
-#define cpu_has_tlb            1
-#define cpu_has_4kex           1
-#define cpu_has_4k_cache       1
-/* #define cpu_has_fpu         ? */
-/* #define cpu_has_32fpr       ? */
-#define cpu_has_counter                1
-/* #define cpu_has_watch       ? */
-#define cpu_has_divec          1
-#define cpu_has_vce            0
-/* #define cpu_has_cache_cdex_p ? */
-/* #define cpu_has_cache_cdex_s ? */
-/* #define cpu_has_prefetch    ? */
-#define cpu_has_mcheck         1
-/* #define cpu_has_ejtag       ? */
-#define cpu_has_llsc           1
-/* #define cpu_has_vtag_icache ? */
-/* #define cpu_has_dc_aliases  ? */
-/* #define cpu_has_ic_fills_f_dc ? */
-#define cpu_has_nofpuex                0
-/* #define cpu_has_64bits      ? */
-/* #define cpu_has_64bit_zero_reg ? */
-/* #define cpu_has_inclusive_pcaches ? */
-#define cpu_icache_snoops_remote_store 1
-#endif
-
-#endif /* __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H */
diff --git a/arch/mips/include/asm/mach-sead3/irq.h b/arch/mips/include/asm/mach-sead3/irq.h
deleted file mode 100644 (file)
index 5d154cf..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef __ASM_MACH_MIPS_IRQ_H
-#define __ASM_MACH_MIPS_IRQ_H
-
-#define NR_IRQS 256
-
-
-#include_next <irq.h>
-
-#endif /* __ASM_MACH_MIPS_IRQ_H */
diff --git a/arch/mips/include/asm/mach-sead3/kernel-entry-init.h b/arch/mips/include/asm/mach-sead3/kernel-entry-init.h
deleted file mode 100644 (file)
index 6cccd4d..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Chris Dearman (chris@mips.com)
- * Copyright (C) 2007 Mips Technologies, Inc.
- */
-#ifndef __ASM_MACH_MIPS_KERNEL_ENTRY_INIT_H
-#define __ASM_MACH_MIPS_KERNEL_ENTRY_INIT_H
-
-       .macro  kernel_entry_setup
-       .endm
-
-/*
- * Do SMP slave processor setup necessary before we can safely execute C code.
- */
-       .macro  smp_slave_setup
-       .endm
-
-#endif /* __ASM_MACH_MIPS_KERNEL_ENTRY_INIT_H */
diff --git a/arch/mips/include/asm/mach-sead3/war.h b/arch/mips/include/asm/mach-sead3/war.h
deleted file mode 100644 (file)
index d068fc4..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org>
- */
-#ifndef __ASM_MIPS_MACH_MIPS_WAR_H
-#define __ASM_MIPS_MACH_MIPS_WAR_H
-
-#define R4600_V1_INDEX_ICACHEOP_WAR    0
-#define R4600_V1_HIT_CACHEOP_WAR       0
-#define R4600_V2_HIT_CACHEOP_WAR       0
-#define R5432_CP0_INTERRUPT_WAR                0
-#define BCM1250_M3_WAR                 0
-#define SIBYTE_1956_WAR                        0
-#define MIPS4K_ICACHE_REFILL_WAR       1
-#define MIPS_CACHE_SYNC_WAR            1
-#define TX49XX_ICACHE_INDEX_INV_WAR    0
-#define ICACHE_REFILLS_WORKAROUND_WAR  1
-#define R10000_LLSC_WAR                        0
-#define MIPS34K_MISSED_ITLB_WAR                0
-
-#endif /* __ASM_MIPS_MACH_MIPS_WAR_H */
diff --git a/arch/mips/include/asm/machine.h b/arch/mips/include/asm/machine.h
new file mode 100644 (file)
index 0000000..6b444cd
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.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 __MIPS_ASM_MACHINE_H__
+#define __MIPS_ASM_MACHINE_H__
+
+#include <linux/libfdt.h>
+#include <linux/of.h>
+
+struct mips_machine {
+       const struct of_device_id *matches;
+       const void *fdt;
+       bool (*detect)(void);
+       const void *(*fixup_fdt)(const void *fdt, const void *match_data);
+       unsigned int (*measure_hpt_freq)(void);
+};
+
+extern long __mips_machines_start;
+extern long __mips_machines_end;
+
+#define MIPS_MACHINE(name)                                             \
+       static const struct mips_machine __mips_mach_##name             \
+               __used __section(.mips.machines.init)
+
+#define for_each_mips_machine(mach)                                    \
+       for ((mach) = (struct mips_machine *)&__mips_machines_start;    \
+            (mach) < (struct mips_machine *)&__mips_machines_end;      \
+            (mach)++)
+
+/**
+ * mips_machine_is_compatible() - check if a machine is compatible with an FDT
+ * @mach: the machine struct to check
+ * @fdt: the FDT to check for compatibility with
+ *
+ * Check whether the given machine @mach is compatible with the given flattened
+ * device tree @fdt, based upon the compatibility property of the root node.
+ *
+ * Return: the device id matched if any, else NULL
+ */
+static inline const struct of_device_id *
+mips_machine_is_compatible(const struct mips_machine *mach, const void *fdt)
+{
+       const struct of_device_id *match;
+
+       if (!mach->matches)
+               return NULL;
+
+       for (match = mach->matches; match->compatible; match++) {
+               if (fdt_node_check_compatible(fdt, 0, match->compatible) == 0)
+                       return match;
+       }
+
+       return NULL;
+}
+
+#endif /* __MIPS_ASM_MACHINE_H__ */
diff --git a/arch/mips/include/asm/mips-boards/sead3int.h b/arch/mips/include/asm/mips-boards/sead3int.h
deleted file mode 100644 (file)
index 8932c7d..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2000,2012 MIPS Technologies, Inc.  All rights reserved.
- *     Douglas Leung <douglas@mips.com>
- *     Steven J. Hill <sjhill@mips.com>
- */
-#ifndef _MIPS_SEAD3INT_H
-#define _MIPS_SEAD3INT_H
-
-#include <linux/irqchip/mips-gic.h>
-
-/* SEAD-3 GIC address space definitions. */
-#define GIC_BASE_ADDR          0x1b1c0000
-#define GIC_ADDRSPACE_SZ       (128 * 1024)
-
-/* CPU interrupt offsets */
-#define CPU_INT_GIC            2
-#define CPU_INT_EHCI           2
-#define CPU_INT_UART0          4
-#define CPU_INT_UART1          4
-#define CPU_INT_NET            6
-
-/* GIC interrupt offsets */
-#define GIC_INT_NET            GIC_SHARED_TO_HWIRQ(0)
-#define GIC_INT_UART1          GIC_SHARED_TO_HWIRQ(2)
-#define GIC_INT_UART0          GIC_SHARED_TO_HWIRQ(3)
-#define GIC_INT_EHCI           GIC_SHARED_TO_HWIRQ(5)
-
-#endif /* !(_MIPS_SEAD3INT_H) */
index 4fafeefe65c2a076a6d5683a498e33e18076bc11..2e4180797b21828c3bc93a76d214b51f94f1392e 100644 (file)
@@ -359,6 +359,7 @@ BUILD_CM_Cx_R_(tcid_8_priority,     0x80)
 /* GCR_Cx_COHERENCE register fields */
 #define CM_GCR_Cx_COHERENCE_COHDOMAINEN_SHF    0
 #define CM_GCR_Cx_COHERENCE_COHDOMAINEN_MSK    (_ULCAST_(0xff) << 0)
+#define CM3_GCR_Cx_COHERENCE_COHEN_MSK         (_ULCAST_(0x1) << 0)
 
 /* GCR_Cx_CONFIG register fields */
 #define CM_GCR_Cx_CONFIG_IOCUTYPE_SHF          10
index cda93aee712c6d894de9c26ec821c747103c1e5d..b4d19c21b62cfb53814917e78ffebb27d1343507 100644 (file)
@@ -57,16 +57,6 @@ typedef enum {
  */
 #define CVMX_HELPER_BOARD_MGMT_IPD_PORT            -10
 
-/**
- * cvmx_override_board_link_get(int ipd_port) is a function
- * pointer. It is meant to allow customization of the process of
- * talking to a PHY to determine link speed. It is called every
- * time a PHY must be polled for link status. Users should set
- * this pointer to a function before calling any cvmx-helper
- * operations.
- */
-extern cvmx_helper_link_info_t(*cvmx_override_board_link_get) (int ipd_port);
-
 /**
  * Return the MII PHY address associated with the given IPD
  * port. A result of -1 means there isn't a MII capable PHY
@@ -85,26 +75,6 @@ extern cvmx_helper_link_info_t(*cvmx_override_board_link_get) (int ipd_port);
  */
 extern int cvmx_helper_board_get_mii_address(int ipd_port);
 
-/**
- * This function as a board specific method of changing the PHY
- * speed, duplex, and autonegotiation. This programs the PHY and
- * not Octeon. This can be used to force Octeon's links to
- * specific settings.
- *
- * @phy_addr:  The address of the PHY to program
- * @link_flags:
- *                 Flags to control autonegotiation.  Bit 0 is autonegotiation
- *                 enable/disable to maintain backward compatibility.
- * @link_info: Link speed to program. If the speed is zero and autonegotiation
- *                 is enabled, all possible negotiation speeds are advertised.
- *
- * Returns Zero on success, negative on failure
- */
-int cvmx_helper_board_link_set_phy(int phy_addr,
-                                  cvmx_helper_board_set_phy_link_flags_types_t
-                                  link_flags,
-                                  cvmx_helper_link_info_t link_info);
-
 /**
  * This function is the board specific method of determining an
  * ethernet ports link speed. Most Octeon boards have Marvell PHYs
diff --git a/arch/mips/include/asm/octeon/cvmx-mdio.h b/arch/mips/include/asm/octeon/cvmx-mdio.h
deleted file mode 100644 (file)
index 9f6a4f3..0000000
+++ /dev/null
@@ -1,506 +0,0 @@
-/***********************license start***************
- * Author: Cavium Networks
- *
- * Contact: support@caviumnetworks.com
- * This file is part of the OCTEON SDK
- *
- * Copyright (c) 2003-2008 Cavium Networks
- *
- * This file 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 file is distributed in the hope that it will be useful, but
- * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this file; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- * or visit http://www.gnu.org/licenses/.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium Networks for more information
- ***********************license end**************************************/
-
-/*
- *
- * Interface to the SMI/MDIO hardware, including support for both IEEE 802.3
- * clause 22 and clause 45 operations.
- *
- */
-
-#ifndef __CVMX_MIO_H__
-#define __CVMX_MIO_H__
-
-#include <asm/octeon/cvmx-smix-defs.h>
-
-/**
- * PHY register 0 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_CONTROL 0
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t reset:1;
-               uint16_t loopback:1;
-               uint16_t speed_lsb:1;
-               uint16_t autoneg_enable:1;
-               uint16_t power_down:1;
-               uint16_t isolate:1;
-               uint16_t restart_autoneg:1;
-               uint16_t duplex:1;
-               uint16_t collision_test:1;
-               uint16_t speed_msb:1;
-               uint16_t unidirectional_enable:1;
-               uint16_t reserved_0_4:5;
-       } s;
-} cvmx_mdio_phy_reg_control_t;
-
-/**
- * PHY register 1 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_STATUS 1
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t capable_100base_t4:1;
-               uint16_t capable_100base_x_full:1;
-               uint16_t capable_100base_x_half:1;
-               uint16_t capable_10_full:1;
-               uint16_t capable_10_half:1;
-               uint16_t capable_100base_t2_full:1;
-               uint16_t capable_100base_t2_half:1;
-               uint16_t capable_extended_status:1;
-               uint16_t capable_unidirectional:1;
-               uint16_t capable_mf_preamble_suppression:1;
-               uint16_t autoneg_complete:1;
-               uint16_t remote_fault:1;
-               uint16_t capable_autoneg:1;
-               uint16_t link_status:1;
-               uint16_t jabber_detect:1;
-               uint16_t capable_extended_registers:1;
-
-       } s;
-} cvmx_mdio_phy_reg_status_t;
-
-/**
- * PHY register 2 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_ID1 2
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t oui_bits_3_18;
-       } s;
-} cvmx_mdio_phy_reg_id1_t;
-
-/**
- * PHY register 3 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_ID2 3
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t oui_bits_19_24:6;
-               uint16_t model:6;
-               uint16_t revision:4;
-       } s;
-} cvmx_mdio_phy_reg_id2_t;
-
-/**
- * PHY register 4 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_AUTONEG_ADVER 4
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t next_page:1;
-               uint16_t reserved_14:1;
-               uint16_t remote_fault:1;
-               uint16_t reserved_12:1;
-               uint16_t asymmetric_pause:1;
-               uint16_t pause:1;
-               uint16_t advert_100base_t4:1;
-               uint16_t advert_100base_tx_full:1;
-               uint16_t advert_100base_tx_half:1;
-               uint16_t advert_10base_tx_full:1;
-               uint16_t advert_10base_tx_half:1;
-               uint16_t selector:5;
-       } s;
-} cvmx_mdio_phy_reg_autoneg_adver_t;
-
-/**
- * PHY register 5 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_LINK_PARTNER_ABILITY 5
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t next_page:1;
-               uint16_t ack:1;
-               uint16_t remote_fault:1;
-               uint16_t reserved_12:1;
-               uint16_t asymmetric_pause:1;
-               uint16_t pause:1;
-               uint16_t advert_100base_t4:1;
-               uint16_t advert_100base_tx_full:1;
-               uint16_t advert_100base_tx_half:1;
-               uint16_t advert_10base_tx_full:1;
-               uint16_t advert_10base_tx_half:1;
-               uint16_t selector:5;
-       } s;
-} cvmx_mdio_phy_reg_link_partner_ability_t;
-
-/**
- * PHY register 6 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_AUTONEG_EXPANSION 6
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t reserved_5_15:11;
-               uint16_t parallel_detection_fault:1;
-               uint16_t link_partner_next_page_capable:1;
-               uint16_t local_next_page_capable:1;
-               uint16_t page_received:1;
-               uint16_t link_partner_autoneg_capable:1;
-
-       } s;
-} cvmx_mdio_phy_reg_autoneg_expansion_t;
-
-/**
- * PHY register 9 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_CONTROL_1000 9
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t test_mode:3;
-               uint16_t manual_master_slave:1;
-               uint16_t master:1;
-               uint16_t port_type:1;
-               uint16_t advert_1000base_t_full:1;
-               uint16_t advert_1000base_t_half:1;
-               uint16_t reserved_0_7:8;
-       } s;
-} cvmx_mdio_phy_reg_control_1000_t;
-
-/**
- * PHY register 10 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_STATUS_1000 10
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t master_slave_fault:1;
-               uint16_t is_master:1;
-               uint16_t local_receiver_ok:1;
-               uint16_t remote_receiver_ok:1;
-               uint16_t remote_capable_1000base_t_full:1;
-               uint16_t remote_capable_1000base_t_half:1;
-               uint16_t reserved_8_9:2;
-               uint16_t idle_error_count:8;
-       } s;
-} cvmx_mdio_phy_reg_status_1000_t;
-
-/**
- * PHY register 15 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_EXTENDED_STATUS 15
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t capable_1000base_x_full:1;
-               uint16_t capable_1000base_x_half:1;
-               uint16_t capable_1000base_t_full:1;
-               uint16_t capable_1000base_t_half:1;
-               uint16_t reserved_0_11:12;
-       } s;
-} cvmx_mdio_phy_reg_extended_status_t;
-
-/**
- * PHY register 13 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_MMD_CONTROL 13
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t function:2;
-               uint16_t reserved_5_13:9;
-               uint16_t devad:5;
-       } s;
-} cvmx_mdio_phy_reg_mmd_control_t;
-
-/**
- * PHY register 14 from the 802.3 spec
- */
-#define CVMX_MDIO_PHY_REG_MMD_ADDRESS_DATA 14
-typedef union {
-       uint16_t u16;
-       struct {
-               uint16_t address_data:16;
-       } s;
-} cvmx_mdio_phy_reg_mmd_address_data_t;
-
-/* Operating request encodings. */
-#define MDIO_CLAUSE_22_WRITE   0
-#define MDIO_CLAUSE_22_READ    1
-
-#define MDIO_CLAUSE_45_ADDRESS 0
-#define MDIO_CLAUSE_45_WRITE   1
-#define MDIO_CLAUSE_45_READ_INC 2
-#define MDIO_CLAUSE_45_READ    3
-
-/* MMD identifiers, mostly for accessing devices within XENPAK modules. */
-#define CVMX_MMD_DEVICE_PMA_PMD             1
-#define CVMX_MMD_DEVICE_WIS         2
-#define CVMX_MMD_DEVICE_PCS         3
-#define CVMX_MMD_DEVICE_PHY_XS      4
-#define CVMX_MMD_DEVICE_DTS_XS      5
-#define CVMX_MMD_DEVICE_TC          6
-#define CVMX_MMD_DEVICE_CL22_EXT     29
-#define CVMX_MMD_DEVICE_VENDOR_1     30
-#define CVMX_MMD_DEVICE_VENDOR_2     31
-
-/* Helper function to put MDIO interface into clause 45 mode */
-static inline void __cvmx_mdio_set_clause45_mode(int bus_id)
-{
-       union cvmx_smix_clk smi_clk;
-       /* Put bus into clause 45 mode */
-       smi_clk.u64 = cvmx_read_csr(CVMX_SMIX_CLK(bus_id));
-       smi_clk.s.mode = 1;
-       smi_clk.s.preamble = 1;
-       cvmx_write_csr(CVMX_SMIX_CLK(bus_id), smi_clk.u64);
-}
-
-/* Helper function to put MDIO interface into clause 22 mode */
-static inline void __cvmx_mdio_set_clause22_mode(int bus_id)
-{
-       union cvmx_smix_clk smi_clk;
-       /* Put bus into clause 22 mode */
-       smi_clk.u64 = cvmx_read_csr(CVMX_SMIX_CLK(bus_id));
-       smi_clk.s.mode = 0;
-       cvmx_write_csr(CVMX_SMIX_CLK(bus_id), smi_clk.u64);
-}
-
-/**
- * Perform an MII read. This function is used to read PHY
- * registers controlling auto negotiation.
- *
- * @bus_id:   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
- *                support multiple busses.
- * @phy_id:   The MII phy id
- * @location: Register location to read
- *
- * Returns Result from the read or -1 on failure
- */
-static inline int cvmx_mdio_read(int bus_id, int phy_id, int location)
-{
-       union cvmx_smix_cmd smi_cmd;
-       union cvmx_smix_rd_dat smi_rd;
-       int timeout = 1000;
-
-       if (octeon_has_feature(OCTEON_FEATURE_MDIO_CLAUSE_45))
-               __cvmx_mdio_set_clause22_mode(bus_id);
-
-       smi_cmd.u64 = 0;
-       smi_cmd.s.phy_op = MDIO_CLAUSE_22_READ;
-       smi_cmd.s.phy_adr = phy_id;
-       smi_cmd.s.reg_adr = location;
-       cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
-
-       do {
-               cvmx_wait(1000);
-               smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(bus_id));
-       } while (smi_rd.s.pending && timeout--);
-
-       if (smi_rd.s.val)
-               return smi_rd.s.dat;
-       else
-               return -1;
-}
-
-/**
- * Perform an MII write. This function is used to write PHY
- * registers controlling auto negotiation.
- *
- * @bus_id:   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
- *                support multiple busses.
- * @phy_id:   The MII phy id
- * @location: Register location to write
- * @val:      Value to write
- *
- * Returns -1 on error
- *        0 on success
- */
-static inline int cvmx_mdio_write(int bus_id, int phy_id, int location, int val)
-{
-       union cvmx_smix_cmd smi_cmd;
-       union cvmx_smix_wr_dat smi_wr;
-       int timeout = 1000;
-
-       if (octeon_has_feature(OCTEON_FEATURE_MDIO_CLAUSE_45))
-               __cvmx_mdio_set_clause22_mode(bus_id);
-
-       smi_wr.u64 = 0;
-       smi_wr.s.dat = val;
-       cvmx_write_csr(CVMX_SMIX_WR_DAT(bus_id), smi_wr.u64);
-
-       smi_cmd.u64 = 0;
-       smi_cmd.s.phy_op = MDIO_CLAUSE_22_WRITE;
-       smi_cmd.s.phy_adr = phy_id;
-       smi_cmd.s.reg_adr = location;
-       cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
-
-       do {
-               cvmx_wait(1000);
-               smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(bus_id));
-       } while (smi_wr.s.pending && --timeout);
-       if (timeout <= 0)
-               return -1;
-
-       return 0;
-}
-
-/**
- * Perform an IEEE 802.3 clause 45 MII read. This function is used to
- * read PHY registers controlling auto negotiation.
- *
- * @bus_id:   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
- *                support multiple busses.
- * @phy_id:   The MII phy id
- * @device:   MDIO Managable Device (MMD) id
- * @location: Register location to read
- *
- * Returns Result from the read or -1 on failure
- */
-
-static inline int cvmx_mdio_45_read(int bus_id, int phy_id, int device,
-                                   int location)
-{
-       union cvmx_smix_cmd smi_cmd;
-       union cvmx_smix_rd_dat smi_rd;
-       union cvmx_smix_wr_dat smi_wr;
-       int timeout = 1000;
-
-       if (!octeon_has_feature(OCTEON_FEATURE_MDIO_CLAUSE_45))
-               return -1;
-
-       __cvmx_mdio_set_clause45_mode(bus_id);
-
-       smi_wr.u64 = 0;
-       smi_wr.s.dat = location;
-       cvmx_write_csr(CVMX_SMIX_WR_DAT(bus_id), smi_wr.u64);
-
-       smi_cmd.u64 = 0;
-       smi_cmd.s.phy_op = MDIO_CLAUSE_45_ADDRESS;
-       smi_cmd.s.phy_adr = phy_id;
-       smi_cmd.s.reg_adr = device;
-       cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
-
-       do {
-               cvmx_wait(1000);
-               smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(bus_id));
-       } while (smi_wr.s.pending && --timeout);
-       if (timeout <= 0) {
-               cvmx_dprintf("cvmx_mdio_45_read: bus_id %d phy_id %2d "
-                            "device %2d register %2d   TIME OUT(address)\n",
-                    bus_id, phy_id, device, location);
-               return -1;
-       }
-
-       smi_cmd.u64 = 0;
-       smi_cmd.s.phy_op = MDIO_CLAUSE_45_READ;
-       smi_cmd.s.phy_adr = phy_id;
-       smi_cmd.s.reg_adr = device;
-       cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
-
-       do {
-               cvmx_wait(1000);
-               smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(bus_id));
-       } while (smi_rd.s.pending && --timeout);
-
-       if (timeout <= 0) {
-               cvmx_dprintf("cvmx_mdio_45_read: bus_id %d phy_id %2d "
-                            "device %2d register %2d   TIME OUT(data)\n",
-                    bus_id, phy_id, device, location);
-               return -1;
-       }
-
-       if (smi_rd.s.val)
-               return smi_rd.s.dat;
-       else {
-               cvmx_dprintf("cvmx_mdio_45_read: bus_id %d phy_id %2d "
-                            "device %2d register %2d   INVALID READ\n",
-                    bus_id, phy_id, device, location);
-               return -1;
-       }
-}
-
-/**
- * Perform an IEEE 802.3 clause 45 MII write. This function is used to
- * write PHY registers controlling auto negotiation.
- *
- * @bus_id:   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
- *                support multiple busses.
- * @phy_id:   The MII phy id
- * @device:   MDIO Managable Device (MMD) id
- * @location: Register location to write
- * @val:      Value to write
- *
- * Returns -1 on error
- *        0 on success
- */
-static inline int cvmx_mdio_45_write(int bus_id, int phy_id, int device,
-                                    int location, int val)
-{
-       union cvmx_smix_cmd smi_cmd;
-       union cvmx_smix_wr_dat smi_wr;
-       int timeout = 1000;
-
-       if (!octeon_has_feature(OCTEON_FEATURE_MDIO_CLAUSE_45))
-               return -1;
-
-       __cvmx_mdio_set_clause45_mode(bus_id);
-
-       smi_wr.u64 = 0;
-       smi_wr.s.dat = location;
-       cvmx_write_csr(CVMX_SMIX_WR_DAT(bus_id), smi_wr.u64);
-
-       smi_cmd.u64 = 0;
-       smi_cmd.s.phy_op = MDIO_CLAUSE_45_ADDRESS;
-       smi_cmd.s.phy_adr = phy_id;
-       smi_cmd.s.reg_adr = device;
-       cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
-
-       do {
-               cvmx_wait(1000);
-               smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(bus_id));
-       } while (smi_wr.s.pending && --timeout);
-       if (timeout <= 0)
-               return -1;
-
-       smi_wr.u64 = 0;
-       smi_wr.s.dat = val;
-       cvmx_write_csr(CVMX_SMIX_WR_DAT(bus_id), smi_wr.u64);
-
-       smi_cmd.u64 = 0;
-       smi_cmd.s.phy_op = MDIO_CLAUSE_45_WRITE;
-       smi_cmd.s.phy_adr = phy_id;
-       smi_cmd.s.reg_adr = device;
-       cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
-
-       do {
-               cvmx_wait(1000);
-               smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(bus_id));
-       } while (smi_wr.s.pending && --timeout);
-       if (timeout <= 0)
-               return -1;
-
-       return 0;
-}
-
-#endif
index 9b63cd41213de1290e7a06c31791a2eae74d7487..30d1129d86248444fc7065fe99bd26c8f5111d0e 100644 (file)
  */
 
 #include <linux/ioport.h>
+#include <linux/list.h>
 #include <linux/of.h>
 
+#ifdef CONFIG_PCI_DRIVERS_LEGACY
+
 /*
  * Each pci channel is a top-level PCI bus seem by CPU.         A machine  with
  * multiple PCI channels may have multiple PCI host controllers or a
  * single controller supporting multiple channels.
  */
 struct pci_controller {
-       struct pci_controller *next;
+       struct list_head list;
        struct pci_bus *bus;
        struct device_node *of_node;
 
@@ -38,10 +41,12 @@ struct pci_controller {
        struct resource *busn_resource;
        unsigned long busn_offset;
 
+#ifndef CONFIG_PCI_DOMAINS_GENERIC
        unsigned int index;
        /* For compatibility with current (as of July 2003) pciutils
           and XFree86. Eventually will be removed. */
        unsigned int need_domain_info;
+#endif
 
        /* Optional access methods for reading/writing the bus number
           of the PCI controller */
@@ -59,12 +64,43 @@ extern void register_pci_controller(struct pci_controller *hose);
  */
 extern int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
 
+/* Do platform specific device initialization at pci_enable_device() time */
+extern int pcibios_plat_dev_init(struct pci_dev *dev);
+
+extern char * (*pcibios_plat_setup)(char *str);
+
+#ifdef CONFIG_OF
+/* this function parses memory ranges from a device node */
+extern void pci_load_of_ranges(struct pci_controller *hose,
+                              struct device_node *node);
+#else
+static inline void pci_load_of_ranges(struct pci_controller *hose,
+                                     struct device_node *node) {}
+#endif
+
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+static inline void set_pci_need_domain_info(struct pci_controller *hose,
+                                           int need_domain_info)
+{
+       /* nothing to do */
+}
+#elif defined(CONFIG_PCI_DOMAINS)
+static inline void set_pci_need_domain_info(struct pci_controller *hose,
+                                           int need_domain_info)
+{
+       hose->need_domain_info = need_domain_info;
+}
+#endif /* CONFIG_PCI_DOMAINS */
+
+#endif
 
 /* Can be used to override the logic in pci_scan_bus for skipping
    already-configured bus numbers - to be used for buggy BIOSes
    or architectures with incomplete PCI setup by the loader */
-
-extern unsigned int pcibios_assign_all_busses(void);
+static inline unsigned int pcibios_assign_all_busses(void)
+{
+       return 1;
+}
 
 extern unsigned long PCIBIOS_MIN_IO;
 extern unsigned long PCIBIOS_MIN_MEM;
@@ -100,7 +136,12 @@ struct pci_dev;
  */
 #define PCI_DMA_BUS_IS_PHYS     (1)
 
-#ifdef CONFIG_PCI_DOMAINS
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+static inline int pci_proc_domain(struct pci_bus *bus)
+{
+       return pci_domain_nr(bus);
+}
+#elif defined(CONFIG_PCI_DOMAINS)
 #define pci_domain_nr(bus) ((struct pci_controller *)(bus)->sysdata)->index
 
 static inline int pci_proc_domain(struct pci_bus *bus)
@@ -121,15 +162,4 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
        return channel ? 15 : 14;
 }
 
-extern char * (*pcibios_plat_setup)(char *str);
-
-#ifdef CONFIG_OF
-/* this function parses memory ranges from a device node */
-extern void pci_load_of_ranges(struct pci_controller *hose,
-                              struct device_node *node);
-#else
-static inline void pci_load_of_ranges(struct pci_controller *hose,
-                                     struct device_node *node) {}
-#endif
-
 #endif /* _ASM_PCI_H */
index 93c079a1cfc8e1f13366590e939c47eade8db789..a03e86969f78a86a9989897ad95cfad1b7798d73 100644 (file)
@@ -67,11 +67,7 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
 static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
        unsigned long address)
 {
-       pte_t *pte;
-
-       pte = (pte_t *) __get_free_pages(GFP_KERNEL|__GFP_ZERO, PTE_ORDER);
-
-       return pte;
+       return (pte_t *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, PTE_ORDER);
 }
 
 static inline struct page *pte_alloc_one(struct mm_struct *mm,
index 625eda53d57166084c611e5286aa0141704a984b..89d58d80b77bb18da88124d03963381922de58d8 100644 (file)
 
 /*
  * The CM & CPC can only handle coherence & power control on a per-core basis,
- * thus in an MT system the VPEs within each core are coupled and can only
+ * thus in an MT system the VP(E)s within each core are coupled and can only
  * enter or exit states requiring CM or CPC assistance in unison.
  */
-#ifdef CONFIG_MIPS_MT
+#if defined(CONFIG_CPU_MIPSR6)
+# define coupled_coherence cpu_has_vp
+#elif defined(CONFIG_MIPS_MT)
 # define coupled_coherence cpu_has_mipsmt
 #else
 # define coupled_coherence 0
index f6fc6aac54963fcebcf7a55e094ec32689923ea7..b6578611dddbfc37e9a7bea36886affe93d87411 100644 (file)
@@ -152,7 +152,7 @@ static inline int is_syscall_success(struct pt_regs *regs)
 
 static inline long regs_return_value(struct pt_regs *regs)
 {
-       if (is_syscall_success(regs))
+       if (is_syscall_success(regs) || !user_mode(regs))
                return regs->regs[2];
        else
                return -regs->regs[2];
index 8bc6c70a40302485834387ea33aea356c8ccd5dd..060f23ff181718fb6b9dec3f3a206f1013452956 100644 (file)
@@ -85,6 +85,20 @@ static inline void __cpu_die(unsigned int cpu)
 extern void play_dead(void);
 #endif
 
+/*
+ * This function will set up the necessary IPIs for Linux to communicate
+ * with the CPUs in mask.
+ * Return 0 on success.
+ */
+int mips_smp_ipi_allocate(const struct cpumask *mask);
+
+/*
+ * This function will free up IPIs allocated with mips_smp_ipi_allocate to the
+ * CPUs in mask, which must be a subset of the IPIs that have been configured.
+ * Return 0 on success.
+ */
+int mips_smp_ipi_free(const struct cpumask *mask);
+
 static inline void arch_send_call_function_single_ipi(int cpu)
 {
        extern struct plat_smp_ops *mp_ops;     /* private */
index 4daf839cd8a8efa38bb5bd35cb0f0a842bffd9e3..89fa5c0b1579cf63ecdaf7bdf097214183a979a5 100644 (file)
@@ -859,7 +859,10 @@ extern size_t __copy_user(void *__to, const void *__from, size_t __n);
        __cu_to = (to);                                                 \
        __cu_from = (from);                                             \
        __cu_len = (n);                                                 \
+                                                                       \
+       check_object_size(__cu_from, __cu_len, true);                   \
        might_fault();                                                  \
+                                                                       \
        if (eva_kernel_access())                                        \
                __cu_len = __invoke_copy_to_kernel(__cu_to, __cu_from,  \
                                                   __cu_len);           \
@@ -880,6 +883,9 @@ extern size_t __copy_user_inatomic(void *__to, const void *__from, size_t __n);
        __cu_to = (to);                                                 \
        __cu_from = (from);                                             \
        __cu_len = (n);                                                 \
+                                                                       \
+       check_object_size(__cu_from, __cu_len, true);                   \
+                                                                       \
        if (eva_kernel_access())                                        \
                __cu_len = __invoke_copy_to_kernel(__cu_to, __cu_from,  \
                                                   __cu_len);           \
@@ -898,6 +904,9 @@ extern size_t __copy_user_inatomic(void *__to, const void *__from, size_t __n);
        __cu_to = (to);                                                 \
        __cu_from = (from);                                             \
        __cu_len = (n);                                                 \
+                                                                       \
+       check_object_size(__cu_to, __cu_len, false);                    \
+                                                                       \
        if (eva_kernel_access())                                        \
                __cu_len = __invoke_copy_from_kernel_inatomic(__cu_to,  \
                                                              __cu_from,\
@@ -932,6 +941,9 @@ extern size_t __copy_user_inatomic(void *__to, const void *__from, size_t __n);
        __cu_to = (to);                                                 \
        __cu_from = (from);                                             \
        __cu_len = (n);                                                 \
+                                                                       \
+       check_object_size(__cu_from, __cu_len, true);                   \
+                                                                       \
        if (eva_kernel_access()) {                                      \
                __cu_len = __invoke_copy_to_kernel(__cu_to,             \
                                                   __cu_from,           \
@@ -1124,6 +1136,9 @@ extern size_t __copy_in_user_eva(void *__to, const void *__from, size_t __n);
        __cu_to = (to);                                                 \
        __cu_from = (from);                                             \
        __cu_len = (n);                                                 \
+                                                                       \
+       check_object_size(__cu_to, __cu_len, false);                    \
+                                                                       \
        if (eva_kernel_access()) {                                      \
                __cu_len = __invoke_copy_from_kernel(__cu_to,           \
                                                     __cu_from,         \
@@ -1162,6 +1177,9 @@ extern size_t __copy_in_user_eva(void *__to, const void *__from, size_t __n);
        __cu_to = (to);                                                 \
        __cu_from = (from);                                             \
        __cu_len = (n);                                                 \
+                                                                       \
+       check_object_size(__cu_to, __cu_len, false);                    \
+                                                                       \
        if (eva_kernel_access()) {                                      \
                __cu_len = __invoke_copy_from_kernel(__cu_to,           \
                                                     __cu_from,         \
index 24ad815c7f38d463f45e90e38417649c91a08f68..3e940dbe02629ad6916f1870d2bf5a57086e89b8 100644 (file)
 #define __NR_copy_file_range           (__NR_Linux + 360)
 #define __NR_preadv2                   (__NR_Linux + 361)
 #define __NR_pwritev2                  (__NR_Linux + 362)
+#define __NR_pkey_mprotect             (__NR_Linux + 363)
+#define __NR_pkey_alloc                        (__NR_Linux + 364)
+#define __NR_pkey_free                 (__NR_Linux + 365)
+
 
 /*
  * Offset of the last Linux o32 flavoured syscall
  */
-#define __NR_Linux_syscalls            362
+#define __NR_Linux_syscalls            365
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
 
 #define __NR_O32_Linux                 4000
-#define __NR_O32_Linux_syscalls                362
+#define __NR_O32_Linux_syscalls                365
 
 #if _MIPS_SIM == _MIPS_SIM_ABI64
 
 #define __NR_copy_file_range           (__NR_Linux + 320)
 #define __NR_preadv2                   (__NR_Linux + 321)
 #define __NR_pwritev2                  (__NR_Linux + 322)
+#define __NR_pkey_mprotect             (__NR_Linux + 323)
+#define __NR_pkey_alloc                        (__NR_Linux + 324)
+#define __NR_pkey_free                 (__NR_Linux + 325)
 
 /*
  * Offset of the last Linux 64-bit flavoured syscall
  */
-#define __NR_Linux_syscalls            322
+#define __NR_Linux_syscalls            325
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
 
 #define __NR_64_Linux                  5000
-#define __NR_64_Linux_syscalls         322
+#define __NR_64_Linux_syscalls         325
 
 #if _MIPS_SIM == _MIPS_SIM_NABI32
 
 #define __NR_copy_file_range           (__NR_Linux + 324)
 #define __NR_preadv2                   (__NR_Linux + 325)
 #define __NR_pwritev2                  (__NR_Linux + 326)
+#define __NR_pkey_mprotect             (__NR_Linux + 327)
+#define __NR_pkey_alloc                        (__NR_Linux + 328)
+#define __NR_pkey_free                 (__NR_Linux + 329)
 
 /*
  * Offset of the last N32 flavoured syscall
  */
-#define __NR_Linux_syscalls            326
+#define __NR_Linux_syscalls            329
 
 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
 
 #define __NR_N32_Linux                 6000
-#define __NR_N32_Linux_syscalls                326
+#define __NR_N32_Linux_syscalls                329
 
 #endif /* _UAPI_ASM_UNISTD_H */
index 58ad63d7eb42413ee1259582fd6b666f699faa99..9c7f3e136d50da8874df0af6749aa2dfe358d61c 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Support for n32 Linux/MIPS ELF binaries.
+ * Author: Ralf Baechle (ralf@linux-mips.org)
  *
  * Copyright (C) 1999, 2001 Ralf Baechle
  * Copyright (C) 1999, 2001 Silicon Graphics, Inc.
@@ -37,7 +38,6 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
 #define ELF_ET_DYN_BASE                (TASK32_SIZE / 3 * 2)
 
 #include <asm/processor.h>
-#include <linux/module.h>
 #include <linux/elfcore.h>
 #include <linux/compat.h>
 #include <linux/math64.h>
@@ -96,12 +96,6 @@ jiffies_to_compat_timeval(unsigned long jiffies, struct compat_timeval *value)
 
 #define ELF_CORE_EFLAGS EF_MIPS_ABI2
 
-MODULE_DESCRIPTION("Binary format loader for compatibility with n32 Linux/MIPS binaries");
-MODULE_AUTHOR("Ralf Baechle (ralf@linux-mips.org)");
-
-#undef MODULE_DESCRIPTION
-#undef MODULE_AUTHOR
-
 #undef TASK_SIZE
 #define TASK_SIZE TASK_SIZE32
 
index 49fb881481f7b6940dfb353dedeb412d2be584bb..1ab34322dd977cdc898475573f26dfb19b326c59 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Support for o32 Linux/MIPS ELF binaries.
+ * Author: Ralf Baechle (ralf@linux-mips.org)
  *
  * Copyright (C) 1999, 2001 Ralf Baechle
  * Copyright (C) 1999, 2001 Silicon Graphics, Inc.
@@ -42,7 +43,6 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
 
 #include <asm/processor.h>
 
-#include <linux/module.h>
 #include <linux/elfcore.h>
 #include <linux/compat.h>
 #include <linux/math64.h>
@@ -99,12 +99,6 @@ jiffies_to_compat_timeval(unsigned long jiffies, struct compat_timeval *value)
        value->tv_usec = rem / NSEC_PER_USEC;
 }
 
-MODULE_DESCRIPTION("Binary format loader for compatibility with o32 Linux/MIPS binaries");
-MODULE_AUTHOR("Ralf Baechle (ralf@linux-mips.org)");
-
-#undef MODULE_DESCRIPTION
-#undef MODULE_AUTHOR
-
 #undef TASK_SIZE
 #define TASK_SIZE TASK_SIZE32
 
index 46c227fc98f5af75bbdd389cbe8eb75b33815470..12c718181e5e3ee5e5cc51cca92bc16f408fbe0a 100644 (file)
@@ -9,7 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <asm/branch.h>
 #include <asm/cpu.h>
 #include <asm/cpu-features.h>
@@ -866,3 +866,37 @@ unaligned:
        force_sig(SIGBUS, current);
        return -EFAULT;
 }
+
+#if (defined CONFIG_KPROBES) || (defined CONFIG_UPROBES)
+
+int __insn_is_compact_branch(union mips_instruction insn)
+{
+       if (!cpu_has_mips_r6)
+               return 0;
+
+       switch (insn.i_format.opcode) {
+       case blezl_op:
+       case bgtzl_op:
+       case blez_op:
+       case bgtz_op:
+               /*
+                * blez[l] and bgtz[l] opcodes with non-zero rt
+                * are MIPS R6 compact branches
+                */
+               if (insn.i_format.rt)
+                       return 1;
+               break;
+       case bc6_op:
+       case balc6_op:
+       case pop10_op:
+       case pop30_op:
+       case pop66_op:
+       case pop76_op:
+               return 1;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__insn_is_compact_branch);
+
+#endif  /* CONFIG_KPROBES || CONFIG_UPROBES */
index 212f46f2014e06b36aed708b26215457be72ece7..f5c8bce70db29cb59430cdf0bd46d4fccb80aee8 100644 (file)
@@ -32,7 +32,8 @@
 #include <asm/ptrace.h>
 #include <asm/branch.h>
 #include <asm/break.h>
-#include <asm/inst.h>
+
+#include "probes-common.h"
 
 static const union mips_instruction breakpoint_insn = {
        .b_format = {
@@ -55,63 +56,7 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 
 static int __kprobes insn_has_delayslot(union mips_instruction insn)
 {
-       switch (insn.i_format.opcode) {
-
-               /*
-                * This group contains:
-                * jr and jalr are in r_format format.
-                */
-       case spec_op:
-               switch (insn.r_format.func) {
-               case jr_op:
-               case jalr_op:
-                       break;
-               default:
-                       goto insn_ok;
-               }
-
-               /*
-                * This group contains:
-                * bltz_op, bgez_op, bltzl_op, bgezl_op,
-                * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
-                */
-       case bcond_op:
-
-               /*
-                * These are unconditional and in j_format.
-                */
-       case jal_op:
-       case j_op:
-
-               /*
-                * These are conditional and in i_format.
-                */
-       case beq_op:
-       case beql_op:
-       case bne_op:
-       case bnel_op:
-       case blez_op:
-       case blezl_op:
-       case bgtz_op:
-       case bgtzl_op:
-
-               /*
-                * These are the FPA/cp1 branch instructions.
-                */
-       case cop1_op:
-
-#ifdef CONFIG_CPU_CAVIUM_OCTEON
-       case lwc2_op: /* This is bbit0 on Octeon */
-       case ldc2_op: /* This is bbit032 on Octeon */
-       case swc2_op: /* This is bbit1 on Octeon */
-       case sdc2_op: /* This is bbit132 on Octeon */
-#endif
-               return 1;
-       default:
-               break;
-       }
-insn_ok:
-       return 0;
+       return __insn_has_delay_slot(insn);
 }
 
 /*
@@ -161,6 +106,12 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
                goto out;
        }
 
+       if (__insn_is_compact_branch(insn)) {
+               pr_notice("Kprobes for compact branches are not supported\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
        /* insn: must be on special executable page on mips. */
        p->ainsn.insn = get_insn_slot();
        if (!p->ainsn.insn) {
index 0b29646bcee770533e4a0d25cf1a50b4cc167b7b..50fb62544df71a62d4457d998de415193c0ad90e 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/utsname.h>
 #include <linux/personality.h>
 #include <linux/dnotify.h>
-#include <linux/module.h>
 #include <linux/binfmts.h>
 #include <linux/security.h>
 #include <linux/compat.h>
index 566b8d2c092c31de6293f245d81b12a899d40622..2a45867d3b4f5ff89636d561379592e8ebabae05 100644 (file)
@@ -52,7 +52,7 @@ static phys_addr_t mips_cpc_phys_base(void)
 int mips_cpc_probe(void)
 {
        phys_addr_t addr;
-       unsigned cpu;
+       unsigned int cpu;
 
        for_each_possible_cpu(cpu)
                spin_lock_init(&per_cpu(cpc_core_lock, cpu));
@@ -70,7 +70,12 @@ int mips_cpc_probe(void)
 
 void mips_cpc_lock_other(unsigned int core)
 {
-       unsigned curr_core;
+       unsigned int curr_core;
+
+       if (mips_cm_revision() >= CM_REV_CM3)
+               /* Systems with CM >= 3 lock the CPC via mips_cm_lock_other */
+               return;
+
        preempt_disable();
        curr_core = current_cpu_data.core;
        spin_lock_irqsave(&per_cpu(cpc_core_lock, curr_core),
@@ -86,7 +91,13 @@ void mips_cpc_lock_other(unsigned int core)
 
 void mips_cpc_unlock_other(void)
 {
-       unsigned curr_core = current_cpu_data.core;
+       unsigned int curr_core;
+
+       if (mips_cm_revision() >= CM_REV_CM3)
+               /* Systems with CM >= 3 lock the CPC via mips_cm_lock_other */
+               return;
+
+       curr_core = current_cpu_data.core;
        spin_unlock_irqrestore(&per_cpu(cpc_core_lock, curr_core),
                               per_cpu(cpc_core_lock_flags, curr_core));
        preempt_enable();
index 0a7e10b5f9e39eb312e0e48b2eab1966df6854f3..22dedd62818ad08d517de10a705da5cf2b9a5279 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/debugfs.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/ptrace.h>
 #include <linux/seq_file.h>
 
index 79850e376ef6387cb3956b9af78169fb12694bde..94627a3a6a0d975b6451508fec88738c7c1fcea5 100644 (file)
@@ -20,6 +20,7 @@
 
 #undef DEBUG
 
+#include <linux/extable.h>
 #include <linux/moduleloader.h>
 #include <linux/elf.h>
 #include <linux/mm.h>
index 5b31a9405ebc69cc7de7607a4ee3c0192e267586..7cf653e214237f75b22200c94f0e47c696be14ae 100644 (file)
@@ -8,6 +8,7 @@
  * option) any later version.
  */
 
+#include <linux/cpuhotplug.h>
 #include <linux/init.h>
 #include <linux/percpu.h>
 #include <linux/slab.h>
@@ -70,13 +71,8 @@ static DEFINE_PER_CPU_ALIGNED(atomic_t, pm_barrier);
 DEFINE_PER_CPU_ALIGNED(struct mips_static_suspend_state, cps_cpu_state);
 
 /* A somewhat arbitrary number of labels & relocs for uasm */
-static struct uasm_label labels[32] __initdata;
-static struct uasm_reloc relocs[32] __initdata;
-
-/* CPU dependant sync types */
-static unsigned stype_intervention;
-static unsigned stype_memory;
-static unsigned stype_ordering;
+static struct uasm_label labels[32];
+static struct uasm_reloc relocs[32];
 
 enum mips_reg {
        zero, at, v0, v1, a0, a1, a2, a3,
@@ -134,7 +130,7 @@ int cps_pm_enter_state(enum cps_pm_state state)
                return -EINVAL;
 
        /* Calculate which coupled CPUs (VPEs) are online */
-#ifdef CONFIG_MIPS_MT
+#if defined(CONFIG_MIPS_MT) || defined(CONFIG_CPU_MIPSR6)
        if (cpu_online(cpu)) {
                cpumask_and(coupled_mask, cpu_online_mask,
                            &cpu_sibling_map[cpu]);
@@ -198,10 +194,10 @@ int cps_pm_enter_state(enum cps_pm_state state)
        return 0;
 }
 
-static void __init cps_gen_cache_routine(u32 **pp, struct uasm_label **pl,
-                                        struct uasm_reloc **pr,
-                                        const struct cache_desc *cache,
-                                        unsigned op, int lbl)
+static void cps_gen_cache_routine(u32 **pp, struct uasm_label **pl,
+                                 struct uasm_reloc **pr,
+                                 const struct cache_desc *cache,
+                                 unsigned op, int lbl)
 {
        unsigned cache_size = cache->ways << cache->waybit;
        unsigned i;
@@ -242,10 +238,10 @@ static void __init cps_gen_cache_routine(u32 **pp, struct uasm_label **pl,
        uasm_i_nop(pp);
 }
 
-static int __init cps_gen_flush_fsb(u32 **pp, struct uasm_label **pl,
-                                   struct uasm_reloc **pr,
-                                   const struct cpuinfo_mips *cpu_info,
-                                   int lbl)
+static int cps_gen_flush_fsb(u32 **pp, struct uasm_label **pl,
+                            struct uasm_reloc **pr,
+                            const struct cpuinfo_mips *cpu_info,
+                            int lbl)
 {
        unsigned i, fsb_size = 8;
        unsigned num_loads = (fsb_size * 3) / 2;
@@ -272,14 +268,9 @@ static int __init cps_gen_flush_fsb(u32 **pp, struct uasm_label **pl,
                /* On older ones it's unavailable */
                return -1;
 
-       /* CPUs which do not require the workaround */
-       case CPU_P5600:
-       case CPU_I6400:
-               return 0;
-
        default:
-               WARN_ONCE(1, "pm-cps: FSB flush unsupported for this CPU\n");
-               return -1;
+               /* Assume that the CPU does not need this workaround */
+               return 0;
        }
 
        /*
@@ -320,8 +311,8 @@ static int __init cps_gen_flush_fsb(u32 **pp, struct uasm_label **pl,
                             i * line_size * line_stride, t0);
        }
 
-       /* Completion barrier */
-       uasm_i_sync(pp, stype_memory);
+       /* Barrier ensuring previous cache invalidates are complete */
+       uasm_i_sync(pp, STYPE_SYNC);
        uasm_i_ehb(pp);
 
        /* Check whether the pipeline stalled due to the FSB being full */
@@ -340,9 +331,9 @@ static int __init cps_gen_flush_fsb(u32 **pp, struct uasm_label **pl,
        return 0;
 }
 
-static void __init cps_gen_set_top_bit(u32 **pp, struct uasm_label **pl,
-                                      struct uasm_reloc **pr,
-                                      unsigned r_addr, int lbl)
+static void cps_gen_set_top_bit(u32 **pp, struct uasm_label **pl,
+                               struct uasm_reloc **pr,
+                               unsigned r_addr, int lbl)
 {
        uasm_i_lui(pp, t0, uasm_rel_hi(0x80000000));
        uasm_build_label(pl, *pp, lbl);
@@ -353,7 +344,7 @@ static void __init cps_gen_set_top_bit(u32 **pp, struct uasm_label **pl,
        uasm_i_nop(pp);
 }
 
-static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
+static void *cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
 {
        struct uasm_label *l = labels;
        struct uasm_reloc *r = relocs;
@@ -411,7 +402,7 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
 
        if (coupled_coherence) {
                /* Increment ready_count */
-               uasm_i_sync(&p, stype_ordering);
+               uasm_i_sync(&p, STYPE_SYNC_MB);
                uasm_build_label(&l, p, lbl_incready);
                uasm_i_ll(&p, t1, 0, r_nc_count);
                uasm_i_addiu(&p, t2, t1, 1);
@@ -419,8 +410,8 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
                uasm_il_beqz(&p, &r, t2, lbl_incready);
                uasm_i_addiu(&p, t1, t1, 1);
 
-               /* Ordering barrier */
-               uasm_i_sync(&p, stype_ordering);
+               /* Barrier ensuring all CPUs see the updated r_nc_count value */
+               uasm_i_sync(&p, STYPE_SYNC_MB);
 
                /*
                 * If this is the last VPE to become ready for non-coherence
@@ -441,7 +432,8 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
                        uasm_i_lw(&p, t0, 0, r_nc_count);
                        uasm_il_bltz(&p, &r, t0, lbl_secondary_cont);
                        uasm_i_ehb(&p);
-                       uasm_i_yield(&p, zero, t1);
+                       if (cpu_has_mipsmt)
+                               uasm_i_yield(&p, zero, t1);
                        uasm_il_b(&p, &r, lbl_poll_cont);
                        uasm_i_nop(&p);
                } else {
@@ -449,8 +441,21 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
                         * The core will lose power & this VPE will not continue
                         * so it can simply halt here.
                         */
-                       uasm_i_addiu(&p, t0, zero, TCHALT_H);
-                       uasm_i_mtc0(&p, t0, 2, 4);
+                       if (cpu_has_mipsmt) {
+                               /* Halt the VPE via C0 tchalt register */
+                               uasm_i_addiu(&p, t0, zero, TCHALT_H);
+                               uasm_i_mtc0(&p, t0, 2, 4);
+                       } else if (cpu_has_vp) {
+                               /* Halt the VP via the CPC VP_STOP register */
+                               unsigned int vpe_id;
+
+                               vpe_id = cpu_vpe_id(&cpu_data[cpu]);
+                               uasm_i_addiu(&p, t0, zero, 1 << vpe_id);
+                               UASM_i_LA(&p, t1, (long)addr_cpc_cl_vp_stop());
+                               uasm_i_sw(&p, t0, 0, t1);
+                       } else {
+                               BUG();
+                       }
                        uasm_build_label(&l, p, lbl_secondary_hang);
                        uasm_il_b(&p, &r, lbl_secondary_hang);
                        uasm_i_nop(&p);
@@ -472,22 +477,24 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
        cps_gen_cache_routine(&p, &l, &r, &cpu_data[cpu].dcache,
                              Index_Writeback_Inv_D, lbl_flushdcache);
 
-       /* Completion barrier */
-       uasm_i_sync(&p, stype_memory);
+       /* Barrier ensuring previous cache invalidates are complete */
+       uasm_i_sync(&p, STYPE_SYNC);
        uasm_i_ehb(&p);
 
-       /*
-        * Disable all but self interventions. The load from COHCTL is defined
-        * by the interAptiv & proAptiv SUMs as ensuring that the operation
-        * resulting from the preceding store is complete.
-        */
-       uasm_i_addiu(&p, t0, zero, 1 << cpu_data[cpu].core);
-       uasm_i_sw(&p, t0, 0, r_pcohctl);
-       uasm_i_lw(&p, t0, 0, r_pcohctl);
-
-       /* Sync to ensure previous interventions are complete */
-       uasm_i_sync(&p, stype_intervention);
-       uasm_i_ehb(&p);
+       if (mips_cm_revision() < CM_REV_CM3) {
+               /*
+               * Disable all but self interventions. The load from COHCTL is
+               * defined by the interAptiv & proAptiv SUMs as ensuring that the
+               *  operation resulting from the preceding store is complete.
+               */
+               uasm_i_addiu(&p, t0, zero, 1 << cpu_data[cpu].core);
+               uasm_i_sw(&p, t0, 0, r_pcohctl);
+               uasm_i_lw(&p, t0, 0, r_pcohctl);
+
+               /* Barrier to ensure write to coherence control is complete */
+               uasm_i_sync(&p, STYPE_SYNC);
+               uasm_i_ehb(&p);
+       }
 
        /* Disable coherence */
        uasm_i_sw(&p, zero, 0, r_pcohctl);
@@ -531,8 +538,8 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
                        goto gen_done;
                }
 
-               /* Completion barrier */
-               uasm_i_sync(&p, stype_memory);
+               /* Barrier to ensure write to CPC command is complete */
+               uasm_i_sync(&p, STYPE_SYNC);
                uasm_i_ehb(&p);
        }
 
@@ -562,26 +569,29 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
         * will run this. The first will actually re-enable coherence & the
         * rest will just be performing a rather unusual nop.
         */
-       uasm_i_addiu(&p, t0, zero, CM_GCR_Cx_COHERENCE_COHDOMAINEN_MSK);
+       uasm_i_addiu(&p, t0, zero, mips_cm_revision() < CM_REV_CM3
+                               ? CM_GCR_Cx_COHERENCE_COHDOMAINEN_MSK
+                               : CM3_GCR_Cx_COHERENCE_COHEN_MSK);
+
        uasm_i_sw(&p, t0, 0, r_pcohctl);
        uasm_i_lw(&p, t0, 0, r_pcohctl);
 
-       /* Completion barrier */
-       uasm_i_sync(&p, stype_memory);
+       /* Barrier to ensure write to coherence control is complete */
+       uasm_i_sync(&p, STYPE_SYNC);
        uasm_i_ehb(&p);
 
        if (coupled_coherence && (state == CPS_PM_NC_WAIT)) {
                /* Decrement ready_count */
                uasm_build_label(&l, p, lbl_decready);
-               uasm_i_sync(&p, stype_ordering);
+               uasm_i_sync(&p, STYPE_SYNC_MB);
                uasm_i_ll(&p, t1, 0, r_nc_count);
                uasm_i_addiu(&p, t2, t1, -1);
                uasm_i_sc(&p, t2, 0, r_nc_count);
                uasm_il_beqz(&p, &r, t2, lbl_decready);
                uasm_i_andi(&p, v0, t1, (1 << fls(smp_num_siblings)) - 1);
 
-               /* Ordering barrier */
-               uasm_i_sync(&p, stype_ordering);
+               /* Barrier ensuring all CPUs see the updated r_nc_count value */
+               uasm_i_sync(&p, STYPE_SYNC_MB);
        }
 
        if (coupled_coherence && (state == CPS_PM_CLOCK_GATED)) {
@@ -602,8 +612,8 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
                 */
                uasm_build_label(&l, p, lbl_secondary_cont);
 
-               /* Ordering barrier */
-               uasm_i_sync(&p, stype_ordering);
+               /* Barrier ensuring all CPUs see the updated r_nc_count value */
+               uasm_i_sync(&p, STYPE_SYNC_MB);
        }
 
        /* The core is coherent, time to return to C code */
@@ -628,7 +638,7 @@ out_err:
        return NULL;
 }
 
-static int __init cps_gen_core_entries(unsigned cpu)
+static int cps_pm_online_cpu(unsigned int cpu)
 {
        enum cps_pm_state state;
        unsigned core = cpu_data[cpu].core;
@@ -670,29 +680,10 @@ static int __init cps_gen_core_entries(unsigned cpu)
 
 static int __init cps_pm_init(void)
 {
-       unsigned cpu;
-       int err;
-
-       /* Detect appropriate sync types for the system */
-       switch (current_cpu_data.cputype) {
-       case CPU_INTERAPTIV:
-       case CPU_PROAPTIV:
-       case CPU_M5150:
-       case CPU_P5600:
-       case CPU_I6400:
-               stype_intervention = 0x2;
-               stype_memory = 0x3;
-               stype_ordering = 0x10;
-               break;
-
-       default:
-               pr_warn("Power management is using heavyweight sync 0\n");
-       }
-
        /* A CM is required for all non-coherent states */
        if (!mips_cm_present()) {
                pr_warn("pm-cps: no CM, non-coherent states unavailable\n");
-               goto out;
+               return 0;
        }
 
        /*
@@ -722,12 +713,7 @@ static int __init cps_pm_init(void)
                pr_warn("pm-cps: no CPC, clock & power gating unavailable\n");
        }
 
-       for_each_present_cpu(cpu) {
-               err = cps_gen_core_entries(cpu);
-               if (err)
-                       return err;
-       }
-out:
-       return 0;
+       return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "AP_PM_CPS_CPU_ONLINE",
+                                cps_pm_online_cpu, NULL);
 }
 arch_initcall(cps_pm_init);
diff --git a/arch/mips/kernel/probes-common.h b/arch/mips/kernel/probes-common.h
new file mode 100644 (file)
index 0000000..dd08e41
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Marcin Nowakowski <marcin.nowakowski@imgtec.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 __PROBES_COMMON_H
+#define __PROBES_COMMON_H
+
+#include <asm/inst.h>
+
+int __insn_is_compact_branch(union mips_instruction insn);
+
+static inline int __insn_has_delay_slot(const union mips_instruction insn)
+{
+       switch (insn.i_format.opcode) {
+       /*
+        * jr and jalr are in r_format format.
+        */
+       case spec_op:
+               switch (insn.r_format.func) {
+               case jalr_op:
+               case jr_op:
+                       return 1;
+               }
+               break;
+
+       /*
+        * This group contains:
+        * bltz_op, bgez_op, bltzl_op, bgezl_op,
+        * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
+        */
+       case bcond_op:
+               switch (insn.i_format.rt) {
+               case bltz_op:
+               case bltzl_op:
+               case bgez_op:
+               case bgezl_op:
+               case bltzal_op:
+               case bltzall_op:
+               case bgezal_op:
+               case bgezall_op:
+               case bposge32_op:
+                       return 1;
+               }
+               break;
+
+       /*
+        * These are unconditional and in j_format.
+        */
+       case jal_op:
+       case j_op:
+       case beq_op:
+       case beql_op:
+       case bne_op:
+       case bnel_op:
+       case blez_op: /* not really i_format */
+       case blezl_op:
+       case bgtz_op:
+       case bgtzl_op:
+               return 1;
+
+       /*
+        * And now the FPA/cp1 branch instructions.
+        */
+       case cop1_op:
+#ifdef CONFIG_CPU_CAVIUM_OCTEON
+       case lwc2_op: /* This is bbit0 on Octeon */
+       case ldc2_op: /* This is bbit032 on Octeon */
+       case swc2_op: /* This is bbit1 on Octeon */
+       case sdc2_op: /* This is bbit132 on Octeon */
+#endif
+               return 1;
+       }
+
+       return 0;
+}
+
+#endif  /* __PROBES_COMMON_H */
index 97dc01b03631196252c118f592e58e7b41bca428..4eff2aed736019d6f071b00487d46bfdc76a7308 100644 (file)
@@ -135,6 +135,13 @@ static int show_cpuinfo(struct seq_file *m, void *v)
        seq_printf(m, "package\t\t\t: %d\n", cpu_data[n].package);
        seq_printf(m, "core\t\t\t: %d\n", cpu_data[n].core);
 
+#if defined(CONFIG_MIPS_MT_SMP) || defined(CONFIG_CPU_MIPSR6)
+       if (cpu_has_mipsmt)
+               seq_printf(m, "VPE\t\t\t: %d\n", cpu_data[n].vpe_id);
+       else if (cpu_has_vp)
+               seq_printf(m, "VP\t\t\t: %d\n", cpu_data[n].vpe_id);
+#endif
+
        sprintf(fmt, "VCE%%c exceptions\t\t: %s\n",
                      cpu_has_vce ? "%u" : "not available");
        seq_printf(m, fmt, 'D', vced_count);
index 283b5a1967d1461298bf065b2073d62c6296dc1d..7e71a4e0281ba9cc3c2190dd9a06d958754155fb 100644 (file)
@@ -70,7 +70,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
                        break;
 
                copied = access_process_vm(child, (u64)addrOthers, &tmp,
-                               sizeof(tmp), 0);
+                               sizeof(tmp), FOLL_FORCE);
                if (copied != sizeof(tmp))
                        break;
                ret = put_user(tmp, (u32 __user *) (unsigned long) data);
@@ -179,7 +179,8 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
                        break;
                ret = 0;
                if (access_process_vm(child, (u64)addrOthers, &data,
-                                       sizeof(data), 1) == sizeof(data))
+                                       sizeof(data),
+                                       FOLL_FORCE | FOLL_WRITE) == sizeof(data))
                        break;
                ret = -EIO;
                break;
index c8e43e0c4066b599fd24b1135eebac0129252d17..c29d397eee86cf48a05d7945efe3cdf4a38a7273 100644 (file)
@@ -597,3 +597,6 @@ EXPORT(sys_call_table)
        PTR     sys_copy_file_range             /* 4360 */
        PTR     sys_preadv2
        PTR     sys_pwritev2
+       PTR     sys_pkey_mprotect
+       PTR     sys_pkey_alloc
+       PTR     sys_pkey_free                   /* 4365 */
index e6ede125059fe87253edb0bd5a5d9e46c8ef0fff..0687f96ee912698285a92abde87a7376897f076b 100644 (file)
@@ -435,4 +435,7 @@ EXPORT(sys_call_table)
        PTR     sys_copy_file_range             /* 5320 */
        PTR     sys_preadv2
        PTR     sys_pwritev2
+       PTR     sys_pkey_mprotect
+       PTR     sys_pkey_alloc
+       PTR     sys_pkey_free                   /* 5325 */
        .size   sys_call_table,.-sys_call_table
index 51d3988933f831bfe8dd7be7282ecd3174b22a89..0331ba39a065b8530818093d7b707921242a3672 100644 (file)
@@ -430,4 +430,7 @@ EXPORT(sysn32_call_table)
        PTR     sys_copy_file_range
        PTR     compat_sys_preadv2              /* 6325 */
        PTR     compat_sys_pwritev2
+       PTR     sys_pkey_mprotect
+       PTR     sys_pkey_alloc
+       PTR     sys_pkey_free
        .size   sysn32_call_table,.-sysn32_call_table
index 6efa7136748f6d72f1f5ea47e43e28eae87e59ea..5a47042dd25f7ae7f93cee8a596f311bf17a9382 100644 (file)
@@ -585,4 +585,7 @@ EXPORT(sys32_call_table)
        PTR     sys_copy_file_range             /* 4360 */
        PTR     compat_sys_preadv2
        PTR     compat_sys_pwritev2
+       PTR     sys_pkey_mprotect
+       PTR     sys_pkey_alloc
+       PTR     sys_pkey_free                   /* 4365 */
        .size   sys32_call_table,.-sys32_call_table
diff --git a/arch/mips/kernel/smp-gic.c b/arch/mips/kernel/smp-gic.c
deleted file mode 100644 (file)
index 9b63829..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2013 Imagination Technologies
- * Author: Paul Burton <paul.burton@imgtec.com>
- *
- * Based on smp-cmp.c:
- *  Copyright (C) 2007 MIPS Technologies, Inc.
- *  Author: Chris Dearman (chris@mips.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/irqchip/mips-gic.h>
-#include <linux/printk.h>
-
-#include <asm/mips-cpc.h>
-#include <asm/smp-ops.h>
-
-void gic_send_ipi_single(int cpu, unsigned int action)
-{
-       unsigned long flags;
-       unsigned int intr;
-       unsigned int core = cpu_data[cpu].core;
-
-       pr_debug("CPU%d: %s cpu %d action %u status %08x\n",
-                smp_processor_id(), __func__, cpu, action, read_c0_status());
-
-       local_irq_save(flags);
-
-       switch (action) {
-       case SMP_CALL_FUNCTION:
-               intr = plat_ipi_call_int_xlate(cpu);
-               break;
-
-       case SMP_RESCHEDULE_YOURSELF:
-               intr = plat_ipi_resched_int_xlate(cpu);
-               break;
-
-       default:
-               BUG();
-       }
-
-       gic_send_ipi(intr);
-
-       if (mips_cpc_present() && (core != current_cpu_data.core)) {
-               while (!cpumask_test_cpu(cpu, &cpu_coherent_mask)) {
-                       mips_cm_lock_other(core, 0);
-                       mips_cpc_lock_other(core);
-                       write_cpc_co_cmd(CPC_Cx_CMD_PWRUP);
-                       mips_cpc_unlock_other();
-                       mips_cm_unlock_other();
-               }
-       }
-
-       local_irq_restore(flags);
-}
-
-void gic_send_ipi_mask(const struct cpumask *mask, unsigned int action)
-{
-       unsigned int i;
-
-       for_each_cpu(i, mask)
-               gic_send_ipi_single(i, action);
-}
index 4f9570a57e8d8a354f48502f92d2d82b5907e3d3..e077ea3e11fb36ee2d5f85f7e8415c97eeead1d9 100644 (file)
@@ -289,26 +289,3 @@ struct plat_smp_ops vsmp_smp_ops = {
        .prepare_cpus           = vsmp_prepare_cpus,
 };
 
-#ifdef CONFIG_PROC_FS
-static int proc_cpuinfo_chain_call(struct notifier_block *nfb,
-       unsigned long action_unused, void *data)
-{
-       struct proc_cpuinfo_notifier_args *pcn = data;
-       struct seq_file *m = pcn->m;
-       unsigned long n = pcn->n;
-
-       if (!cpu_has_mipsmt)
-               return NOTIFY_OK;
-
-       seq_printf(m, "VPE\t\t\t: %d\n", cpu_data[n].vpe_id);
-
-       return NOTIFY_OK;
-}
-
-static int __init proc_cpuinfo_notifier_init(void)
-{
-       return proc_cpuinfo_notifier(proc_cpuinfo_chain_call, 0);
-}
-
-subsys_initcall(proc_cpuinfo_notifier_init);
-#endif
index b0baf48951faabffac7a53fde37dc79748dea0ac..7ebb1918e2ac8abb5a2f8bff3b7d0f51cd126ee8 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/smp.h>
 #include <linux/spinlock.h>
 #include <linux/threads.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/time.h>
 #include <linux/timex.h>
 #include <linux/sched.h>
@@ -192,9 +192,11 @@ void mips_smp_send_ipi_mask(const struct cpumask *mask, unsigned int action)
                                continue;
 
                        while (!cpumask_test_cpu(cpu, &cpu_coherent_mask)) {
+                               mips_cm_lock_other(core, 0);
                                mips_cpc_lock_other(core);
                                write_cpc_co_cmd(CPC_Cx_CMD_PWRUP);
                                mips_cpc_unlock_other();
+                               mips_cm_unlock_other();
                        }
                }
        }
@@ -229,7 +231,7 @@ static struct irqaction irq_call = {
        .name           = "IPI call"
 };
 
-static __init void smp_ipi_init_one(unsigned int virq,
+static void smp_ipi_init_one(unsigned int virq,
                                    struct irqaction *action)
 {
        int ret;
@@ -239,9 +241,11 @@ static __init void smp_ipi_init_one(unsigned int virq,
        BUG_ON(ret);
 }
 
-static int __init mips_smp_ipi_init(void)
+static unsigned int call_virq, sched_virq;
+
+int mips_smp_ipi_allocate(const struct cpumask *mask)
 {
-       unsigned int call_virq, sched_virq;
+       int virq;
        struct irq_domain *ipidomain;
        struct device_node *node;
 
@@ -268,16 +272,20 @@ static int __init mips_smp_ipi_init(void)
        if (!ipidomain)
                return 0;
 
-       call_virq = irq_reserve_ipi(ipidomain, cpu_possible_mask);
-       BUG_ON(!call_virq);
+       virq = irq_reserve_ipi(ipidomain, mask);
+       BUG_ON(!virq);
+       if (!call_virq)
+               call_virq = virq;
 
-       sched_virq = irq_reserve_ipi(ipidomain, cpu_possible_mask);
-       BUG_ON(!sched_virq);
+       virq = irq_reserve_ipi(ipidomain, mask);
+       BUG_ON(!virq);
+       if (!sched_virq)
+               sched_virq = virq;
 
        if (irq_domain_is_ipi_per_cpu(ipidomain)) {
                int cpu;
 
-               for_each_cpu(cpu, cpu_possible_mask) {
+               for_each_cpu(cpu, mask) {
                        smp_ipi_init_one(call_virq + cpu, &irq_call);
                        smp_ipi_init_one(sched_virq + cpu, &irq_resched);
                }
@@ -286,6 +294,45 @@ static int __init mips_smp_ipi_init(void)
                smp_ipi_init_one(sched_virq, &irq_resched);
        }
 
+       return 0;
+}
+
+int mips_smp_ipi_free(const struct cpumask *mask)
+{
+       struct irq_domain *ipidomain;
+       struct device_node *node;
+
+       node = of_irq_find_parent(of_root);
+       ipidomain = irq_find_matching_host(node, DOMAIN_BUS_IPI);
+
+       /*
+        * Some platforms have half DT setup. So if we found irq node but
+        * didn't find an ipidomain, try to search for one that is not in the
+        * DT.
+        */
+       if (node && !ipidomain)
+               ipidomain = irq_find_matching_host(NULL, DOMAIN_BUS_IPI);
+
+       BUG_ON(!ipidomain);
+
+       if (irq_domain_is_ipi_per_cpu(ipidomain)) {
+               int cpu;
+
+               for_each_cpu(cpu, mask) {
+                       remove_irq(call_virq + cpu, &irq_call);
+                       remove_irq(sched_virq + cpu, &irq_resched);
+               }
+       }
+       irq_destroy_ipi(call_virq, mask);
+       irq_destroy_ipi(sched_virq, mask);
+       return 0;
+}
+
+
+static int __init mips_smp_ipi_init(void)
+{
+       mips_smp_ipi_allocate(cpu_possible_mask);
+
        call_desc = irq_to_desc(call_virq);
        sched_desc = irq_to_desc(sched_virq);
 
index 3de85be2486a44d5fbaedb64a556b3e9bf879210..1f5fdee1dfc31a1be2335bebe79de934c0e2d065 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/extable.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/smp.h>
@@ -48,6 +49,7 @@
 #include <asm/fpu.h>
 #include <asm/fpu_emulator.h>
 #include <asm/idle.h>
+#include <asm/mips-cm.h>
 #include <asm/mips-r2-to-r6-emul.h>
 #include <asm/mipsregs.h>
 #include <asm/mipsmtregs.h>
@@ -444,6 +446,8 @@ asmlinkage void do_be(struct pt_regs *regs)
 
        if (board_be_handler)
                action = board_be_handler(regs, fixup != NULL);
+       else
+               mips_cm_error_report();
 
        switch (action) {
        case MIPS_BE_DISCARD:
@@ -2091,6 +2095,14 @@ static void configure_exception_vector(void)
 {
        if (cpu_has_veic || cpu_has_vint) {
                unsigned long sr = set_c0_status(ST0_BEV);
+               /* If available, use WG to set top bits of EBASE */
+               if (cpu_has_ebase_wg) {
+#ifdef CONFIG_64BIT
+                       write_c0_ebase_64(ebase | MIPS_EBASE_WG);
+#else
+                       write_c0_ebase(ebase | MIPS_EBASE_WG);
+#endif
+               }
                write_c0_ebase(ebase);
                write_c0_status(sr);
                /* Setting vector spacing enables EI/VI mode  */
@@ -2127,8 +2139,17 @@ void per_cpu_trap_init(bool is_boot_cpu)
                 * We shouldn't trust a secondary core has a sane EBASE register
                 * so use the one calculated by the boot CPU.
                 */
-               if (!is_boot_cpu)
+               if (!is_boot_cpu) {
+                       /* If available, use WG to set top bits of EBASE */
+                       if (cpu_has_ebase_wg) {
+#ifdef CONFIG_64BIT
+                               write_c0_ebase_64(ebase | MIPS_EBASE_WG);
+#else
+                               write_c0_ebase(ebase | MIPS_EBASE_WG);
+#endif
+                       }
                        write_c0_ebase(ebase);
+               }
 
                cp0_compare_irq_shift = CAUSEB_TI - CAUSEB_IP;
                cp0_compare_irq = (read_c0_intctl() >> INTCTLB_IPTI) & 7;
@@ -2209,13 +2230,39 @@ void __init trap_init(void)
 
        if (cpu_has_veic || cpu_has_vint) {
                unsigned long size = 0x200 + VECTORSPACING*64;
+               phys_addr_t ebase_pa;
+
                ebase = (unsigned long)
                        __alloc_bootmem(size, 1 << fls(size), 0);
+
+               /*
+                * Try to ensure ebase resides in KSeg0 if possible.
+                *
+                * It shouldn't generally be in XKPhys on MIPS64 to avoid
+                * hitting a poorly defined exception base for Cache Errors.
+                * The allocation is likely to be in the low 512MB of physical,
+                * in which case we should be able to convert to KSeg0.
+                *
+                * EVA is special though as it allows segments to be rearranged
+                * and to become uncached during cache error handling.
+                */
+               ebase_pa = __pa(ebase);
+               if (!IS_ENABLED(CONFIG_EVA) && !WARN_ON(ebase_pa >= 0x20000000))
+                       ebase = CKSEG0ADDR(ebase_pa);
        } else {
                ebase = CAC_BASE;
 
-               if (cpu_has_mips_r2_r6)
-                       ebase += (read_c0_ebase() & 0x3ffff000);
+               if (cpu_has_mips_r2_r6) {
+                       if (cpu_has_ebase_wg) {
+#ifdef CONFIG_64BIT
+                               ebase = (read_c0_ebase_64() & ~0xfff);
+#else
+                               ebase = (read_c0_ebase() & ~0xfff);
+#endif
+                       } else {
+                               ebase += (read_c0_ebase() & 0x3ffff000);
+                       }
+               }
        }
 
        if (cpu_has_mmips) {
index 4c7c1558944a2a5382c4c8076866607c0064d392..dbb917403131441369c0ecf1c2a8324644958e85 100644 (file)
@@ -8,71 +8,12 @@
 #include <asm/branch.h>
 #include <asm/cpu-features.h>
 #include <asm/ptrace.h>
-#include <asm/inst.h>
+
+#include "probes-common.h"
 
 static inline int insn_has_delay_slot(const union mips_instruction insn)
 {
-       switch (insn.i_format.opcode) {
-       /*
-        * jr and jalr are in r_format format.
-        */
-       case spec_op:
-               switch (insn.r_format.func) {
-               case jalr_op:
-               case jr_op:
-                       return 1;
-               }
-               break;
-
-       /*
-        * This group contains:
-        * bltz_op, bgez_op, bltzl_op, bgezl_op,
-        * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
-        */
-       case bcond_op:
-               switch (insn.i_format.rt) {
-               case bltz_op:
-               case bltzl_op:
-               case bgez_op:
-               case bgezl_op:
-               case bltzal_op:
-               case bltzall_op:
-               case bgezal_op:
-               case bgezall_op:
-               case bposge32_op:
-                       return 1;
-               }
-               break;
-
-       /*
-        * These are unconditional and in j_format.
-        */
-       case jal_op:
-       case j_op:
-       case beq_op:
-       case beql_op:
-       case bne_op:
-       case bnel_op:
-       case blez_op: /* not really i_format */
-       case blezl_op:
-       case bgtz_op:
-       case bgtzl_op:
-               return 1;
-
-       /*
-        * And now the FPA/cp1 branch instructions.
-        */
-       case cop1_op:
-#ifdef CONFIG_CPU_CAVIUM_OCTEON
-       case lwc2_op: /* This is bbit0 on Octeon */
-       case ldc2_op: /* This is bbit032 on Octeon */
-       case swc2_op: /* This is bbit1 on Octeon */
-       case sdc2_op: /* This is bbit132 on Octeon */
-#endif
-               return 1;
-       }
-
-       return 0;
+       return __insn_has_delay_slot(insn);
 }
 
 /**
@@ -95,6 +36,12 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *aup,
                return -EINVAL;
 
        inst.word = aup->insn[0];
+
+       if (__insn_is_compact_branch(inst)) {
+               pr_notice("Uprobes for compact branches are not supported\n");
+               return -EINVAL;
+       }
+
        aup->ixol[0] = aup->insn[insn_has_delay_slot(inst)];
        aup->ixol[1] = UPROBE_BRK_UPROBE_XOL;           /* NOP  */
 
@@ -282,19 +229,14 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
 void __weak arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
                                  void *src, unsigned long len)
 {
-       void *kaddr;
+       unsigned long kaddr, kstart;
 
        /* Initialize the slot */
-       kaddr = kmap_atomic(page);
-       memcpy(kaddr + (vaddr & ~PAGE_MASK), src, len);
-       kunmap_atomic(kaddr);
-
-       /*
-        * The MIPS version of flush_icache_range will operate safely on
-        * user space addresses and more importantly, it doesn't require a
-        * VMA argument.
-        */
-       flush_icache_range(vaddr, vaddr + len);
+       kaddr = (unsigned long)kmap_atomic(page);
+       kstart = kaddr + (vaddr & ~PAGE_MASK);
+       memcpy((void *)kstart, src, len);
+       flush_icache_range(kstart, kstart + len);
+       kunmap_atomic((void *)kaddr);
 }
 
 /**
index a36b77e1705c5839663585d1da910338ae23278c..f43629979a0e59959db7f1732233ad2db538ca5d 100644 (file)
@@ -12,7 +12,6 @@
 
 #include <linux/errno.h>
 #include <linux/err.h>
-#include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
 #include <linux/bootmem.h>
index d280894915ed0dd780052f6a99c58a2172f5fcd2..010cef2406880e8742a389575d9c54bb1c004da7 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/err.h>
 #include <linux/highmem.h>
 #include <linux/kvm_host.h>
-#include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
 #include <linux/bootmem.h>
@@ -45,8 +44,8 @@ static int kvm_mips_trans_replace(struct kvm_vcpu *vcpu, u32 *opc,
        } else if (KVM_GUEST_KSEGX((unsigned long) opc) == KVM_GUEST_KSEG23) {
                local_irq_save(flags);
                memcpy((void *)opc, (void *)&replace, sizeof(u32));
-               local_flush_icache_range((unsigned long)opc,
-                                        (unsigned long)opc + 32);
+               __local_flush_icache_user_range((unsigned long)opc,
+                                               (unsigned long)opc + 32);
                local_irq_restore(flags);
        } else {
                kvm_err("%s: Invalid address: %p\n", __func__, opc);
index 4db4c03708590f3030bdf84d87264ad2a79f6bc4..8770f32c9e0bed601898dcbe3ac6504adaee8953 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/err.h>
 #include <linux/ktime.h>
 #include <linux/kvm_host.h>
-#include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
 #include <linux/bootmem.h>
index ad28dac6b7e9557346813a02f4699cbcdd5b6d74..e88403b3dcdd5d0bf3ecf9d35555bbcb345e50ed 100644 (file)
@@ -11,7 +11,6 @@
 
 #include <linux/errno.h>
 #include <linux/err.h>
-#include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
 #include <linux/bootmem.h>
index 3a5484f9aa5078a3c5a67b35c2e56b8cbd09c35b..3b20441f2bebfb4516f580e6301f3e269e91465f 100644 (file)
@@ -11,7 +11,6 @@
 
 #include <linux/errno.h>
 #include <linux/err.h>
-#include <linux/module.h>
 #include <linux/vmalloc.h>
 
 #include <linux/kvm_host.h>
index 4625495f9230aa97dbdf2d51b8eb9157d1debcbc..577ec81b557dcfa2d4805ed39cbcaffb1f7052d1 100644 (file)
@@ -6,7 +6,7 @@
  *  Copyright (C) 2012 John Crispin <john@phrozen.org>
  */
 
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
 #include <linux/dma-mapping.h>
@@ -55,7 +55,6 @@ static const struct of_device_id vmmc_match[] = {
        { .compatible = "lantiq,vmmc-xway" },
        {},
 };
-MODULE_DEVICE_TABLE(of, vmmc_match);
 
 static struct platform_driver vmmc_driver = {
        .probe = vmmc_probe,
@@ -64,5 +63,4 @@ static struct platform_driver vmmc_driver = {
                .of_match_table = vmmc_match,
        },
 };
-
-module_platform_driver(vmmc_driver);
+builtin_platform_driver(vmmc_driver);
index 71e518c1e7e7500782420ee2c68279d178dc9694..f0a0f2d431b2335fc9f851a41766ec089ea7073d 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * Lantiq XRX200 PHY Firmware Loader
+ * Author: John Crispin
+ *
  *  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.
@@ -8,7 +11,6 @@
 
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
-#include <linux/module.h>
 #include <linux/firmware.h>
 #include <linux/of_platform.h>
 
@@ -100,7 +102,6 @@ static const struct of_device_id xway_phy_match[] = {
        { .compatible = "lantiq,phy-xrx200" },
        {},
 };
-MODULE_DEVICE_TABLE(of, xway_phy_match);
 
 static struct platform_driver xway_phy_driver = {
        .probe = xway_phy_fw_probe,
@@ -109,9 +110,4 @@ static struct platform_driver xway_phy_driver = {
                .of_match_table = xway_phy_match,
        },
 };
-
-module_platform_driver(xway_phy_driver);
-
-MODULE_AUTHOR("John Crispin <john@phrozen.org>");
-MODULE_DESCRIPTION("Lantiq XRX200 PHY Firmware Loader");
-MODULE_LICENSE("GPL");
+builtin_platform_driver(xway_phy_driver);
index 927dc94a030f3942dd72723ec080486654c14846..c3e22053d13eb2172374c90c2a8eb75cb0299784 100644 (file)
@@ -1,4 +1,4 @@
-#include <linux/module.h>
+#include <linux/export.h>
 
 #include "libgcc.h"
 
index 9fdf1a598428a5804937be3515c42cabee401e00..17456024873d20918cc151c16e4d75173b6869ee 100644 (file)
@@ -1,4 +1,4 @@
-#include <linux/module.h>
+#include <linux/export.h>
 
 #include "libgcc.h"
 
index e3e77aa52c957d7971976de7f7a6b5839daee989..a8114148f82a4f4c300c064013b0068f3273191f 100644 (file)
@@ -1,4 +1,5 @@
-#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/compiler.h>
 
 unsigned long long notrace __bswapdi2(unsigned long long u)
 {
index 530a8afe6fda20e345dff0d1e05ff8035cc0950a..106fd978317d3b35740e62f4f0f00979011b050e 100644 (file)
@@ -1,4 +1,5 @@
-#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/compiler.h>
 
 unsigned int notrace __bswapsi2(unsigned int u)
 {
index 06857da96993c2474879d4f2f3bed382fbf141a1..9d849d8743c953d5f1ad9976af7314bd85c31072 100644 (file)
@@ -1,4 +1,4 @@
-#include <linux/module.h>
+#include <linux/export.h>
 
 #include "libgcc.h"
 
index 21d27c6819a2fd813e56bde24924950abdcd9004..2307a3cb2714fca0819cfb7727eca41650f51fbf 100644 (file)
@@ -8,7 +8,7 @@
  * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
  * Copyright (C) 2007, 2014 Maciej W. Rozycki
  */
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/param.h>
 #include <linux/smp.h>
 #include <linux/stringify.h>
index fd35daa45314a370b89f521e3ec401d39e5dfdc9..8ed3f25a9047d06b80cfeffa495cb2f18984a306 100644 (file)
@@ -7,9 +7,11 @@
  *     written by Ralf Baechle <ralf@linux-mips.org>
  */
 #include <linux/pci.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <asm/io.h>
 
+#ifdef CONFIG_PCI_DRIVERS_LEGACY
+
 void __iomem *__pci_ioport_map(struct pci_dev *dev,
                               unsigned long port, unsigned int nr)
 {
@@ -40,6 +42,8 @@ void __iomem *__pci_ioport_map(struct pci_dev *dev,
        return (void __iomem *) (ctrl->io_map_base + port);
 }
 
+#endif /* CONFIG_PCI_DRIVERS_LEGACY */
+
 void pci_iounmap(struct pci_dev *dev, void __iomem * addr)
 {
        iounmap(addr);
index 8e7e378ce51c323a34a8e9d0aa28a49b95ba90cd..9daa92428e23b39b33c6b2c3a63670c11755e5f2 100644 (file)
@@ -6,7 +6,7 @@
  * (C) Copyright 2007 MIPS Technologies, Inc.
  *     written by Ralf Baechle <ralf@linux-mips.org>
  */
-#include <linux/module.h>
+#include <linux/export.h>
 #include <asm/io.h>
 
 /*
index 364547449c6532ffb7e322e736e484cd5b2fa40f..221167c1be551aa1b950f038a070e7f92b78a84e 100644 (file)
@@ -1,4 +1,4 @@
-#include <linux/module.h>
+#include <linux/export.h>
 
 #include "libgcc.h"
 
index bd599f58234c9558089acb28f37ac677bd6818dc..08067fa538f2dd070450821def7cb976460aba56 100644 (file)
@@ -1,4 +1,4 @@
-#include <linux/module.h>
+#include <linux/export.h>
 
 #include "libgcc.h"
 
index 7704f20529d63589fce06b73e9fdf1a819f28be8..3c0c2f2096cd8d1862e5807460d5deb39e3ab3fb 100644 (file)
@@ -19,6 +19,21 @@ config LOONGSON1_LS1B
        select USE_GENERIC_EARLY_PRINTK_8250
        select COMMON_CLK
 
+config LOONGSON1_LS1C
+       bool "Loongson LS1C board"
+       select CEVT_R4K if !MIPS_EXTERNAL_TIMER
+       select CSRC_R4K if !MIPS_EXTERNAL_TIMER
+       select SYS_HAS_CPU_LOONGSON1C
+       select DMA_NONCOHERENT
+       select BOOT_ELF32
+       select IRQ_MIPS_CPU
+       select SYS_SUPPORTS_32BIT_KERNEL
+       select SYS_SUPPORTS_LITTLE_ENDIAN
+       select SYS_SUPPORTS_HIGHMEM
+       select SYS_SUPPORTS_MIPS16
+       select SYS_HAS_EARLY_PRINTK
+       select USE_GENERIC_EARLY_PRINTK_8250
+       select COMMON_CLK
 endchoice
 
 menuconfig CEVT_CSRC_LS1X
index 5f4bd6e071ca06410db2e09f5cc400b3ab769d43..1ab2c5bbc06606d9e7c984fd8410647c1698e999 100644 (file)
@@ -9,3 +9,9 @@ obj-$(CONFIG_MACH_LOONGSON32) += common/
 #
 
 obj-$(CONFIG_LOONGSON1_LS1B)  += ls1b/
+
+#
+# Loongson LS1C board
+#
+
+obj-$(CONFIG_LOONGSON1_LS1C)  += ls1c/
index ebb6dc290f0ab5591da3076dfc387a660c786c4b..ffe01c6d0037db032e84344d9eb4682c9d081fb4 100644 (file)
@@ -5,3 +5,4 @@ cflags-$(CONFIG_CPU_LOONGSON1)  += \
 platform-$(CONFIG_MACH_LOONGSON32)     += loongson32/
 cflags-$(CONFIG_MACH_LOONGSON32)       += -I$(srctree)/arch/mips/include/asm/mach-loongson32
 load-$(CONFIG_LOONGSON1_LS1B)          += 0xffffffff80100000
+load-$(CONFIG_LOONGSON1_LS1C)          += 0xffffffff80100000
index 455a7704a90f98a73820e6fa691e991faa2baa60..635a4abe1f4821d990533d0f53508b871a62ff28 100644 (file)
@@ -62,12 +62,58 @@ static void ls1x_irq_unmask(struct irq_data *d)
                        | (1 << bit), LS1X_INTC_INTIEN(n));
 }
 
+static int ls1x_irq_settype(struct irq_data *d, unsigned int type)
+{
+       unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
+       unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
+
+       switch (type) {
+       case IRQ_TYPE_LEVEL_HIGH:
+               __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
+                       | (1 << bit), LS1X_INTC_INTPOL(n));
+               __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
+                       & ~(1 << bit), LS1X_INTC_INTEDGE(n));
+               break;
+       case IRQ_TYPE_LEVEL_LOW:
+               __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
+                       & ~(1 << bit), LS1X_INTC_INTPOL(n));
+               __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
+                       & ~(1 << bit), LS1X_INTC_INTEDGE(n));
+               break;
+       case IRQ_TYPE_EDGE_RISING:
+               __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
+                       | (1 << bit), LS1X_INTC_INTPOL(n));
+               __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
+                       | (1 << bit), LS1X_INTC_INTEDGE(n));
+               break;
+       case IRQ_TYPE_EDGE_FALLING:
+               __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
+                       & ~(1 << bit), LS1X_INTC_INTPOL(n));
+               __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
+                       | (1 << bit), LS1X_INTC_INTEDGE(n));
+               break;
+       case IRQ_TYPE_EDGE_BOTH:
+               __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
+                       & ~(1 << bit), LS1X_INTC_INTPOL(n));
+               __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
+                       | (1 << bit), LS1X_INTC_INTEDGE(n));
+               break;
+       case IRQ_TYPE_NONE:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static struct irq_chip ls1x_irq_chip = {
        .name           = "LS1X-INTC",
        .irq_ack        = ls1x_irq_ack,
        .irq_mask       = ls1x_irq_mask,
        .irq_mask_ack   = ls1x_irq_mask_ack,
        .irq_unmask     = ls1x_irq_unmask,
+       .irq_set_type   = ls1x_irq_settype,
 };
 
 static void ls1x_irq_dispatch(int n)
@@ -107,7 +153,7 @@ asmlinkage void plat_irq_dispatch(void)
 
 }
 
-struct irqaction cascade_irqaction = {
+static struct irqaction cascade_irqaction = {
        .handler = no_action,
        .name = "cascade",
        .flags = IRQF_NO_THREAD,
@@ -120,7 +166,7 @@ static void __init ls1x_irq_init(int base)
        /* Disable interrupts and clear pending,
         * setup all IRQs as high level triggered
         */
-       for (n = 0; n < 4; n++) {
+       for (n = 0; n < INTN; n++) {
                __raw_writel(0x0, LS1X_INTC_INTIEN(n));
                __raw_writel(0xffffffff, LS1X_INTC_INTCLR(n));
                __raw_writel(0xffffffff, LS1X_INTC_INTPOL(n));
@@ -129,7 +175,7 @@ static void __init ls1x_irq_init(int base)
        }
 
 
-       for (n = base; n < LS1X_IRQS; n++) {
+       for (n = base; n < NR_IRQS; n++) {
                irq_set_chip_and_handler(n, &ls1x_irq_chip,
                                         handle_level_irq);
        }
@@ -138,6 +184,9 @@ static void __init ls1x_irq_init(int base)
        setup_irq(INT1_IRQ, &cascade_irqaction);
        setup_irq(INT2_IRQ, &cascade_irqaction);
        setup_irq(INT3_IRQ, &cascade_irqaction);
+#if defined(CONFIG_LOONGSON1_LS1C)
+       setup_irq(INT4_IRQ, &cascade_irqaction);
+#endif
 }
 
 void __init arch_init_irq(void)
index f2c714d8fb60c7654274cd1e1ee604954a86e6c4..beff0852c6a479ef47911ea69e489c68999256fd 100644 (file)
 #include <linux/stmmac.h>
 #include <linux/usb/ehci_pdriver.h>
 
+#include <platform.h>
 #include <loongson1.h>
 #include <cpufreq.h>
 #include <dma.h>
 #include <nand.h>
 
+#define LS1X_RTC_CTRL  ((void __iomem *)KSEG1ADDR(LS1X_RTC_BASE + 0x40))
+#define RTC_EXTCLK_OK  (BIT(5) | BIT(8))
+#define RTC_EXTCLK_EN  BIT(8)
+
 /* 8250/16550 compatible UART */
 #define LS1X_UART(_id)                                         \
        {                                                       \
@@ -65,6 +70,15 @@ void __init ls1x_serial_set_uartclk(struct platform_device *pdev)
                p->uartclk = clk_get_rate(clk);
 }
 
+void __init ls1x_rtc_set_extclk(struct platform_device *pdev)
+{
+       u32 val;
+
+       val = __raw_readl(LS1X_RTC_CTRL);
+       if (!(val & RTC_EXTCLK_OK))
+               __raw_writel(val | RTC_EXTCLK_EN, LS1X_RTC_CTRL);
+}
+
 /* CPUFreq */
 static struct plat_ls1x_cpufreq ls1x_cpufreq_pdata = {
        .clk_name       = "cpu_clk",
@@ -132,6 +146,7 @@ int ls1x_eth_mux_init(struct platform_device *pdev, void *priv)
 
        val = __raw_readl(LS1X_MUX_CTRL1);
 
+#if defined(CONFIG_LOONGSON1_LS1B)
        plat_dat = dev_get_platdata(&pdev->dev);
        if (plat_dat->bus_id) {
                __raw_writel(__raw_readl(LS1X_MUX_CTRL0) | GMAC1_USE_UART1 |
@@ -165,6 +180,17 @@ int ls1x_eth_mux_init(struct platform_device *pdev, void *priv)
                val &= ~GMAC0_SHUT;
        }
        __raw_writel(val, LS1X_MUX_CTRL1);
+#elif defined(CONFIG_LOONGSON1_LS1C)
+       plat_dat = dev_get_platdata(&pdev->dev);
+
+       val &= ~PHY_INTF_SELI;
+       if (plat_dat->interface == PHY_INTERFACE_MODE_RMII)
+               val |= 0x4 << PHY_INTF_SELI_SHIFT;
+       __raw_writel(val, LS1X_MUX_CTRL1);
+
+       val = __raw_readl(LS1X_MUX_CTRL0);
+       __raw_writel(val & (~GMAC_SHUT), LS1X_MUX_CTRL0);
+#endif
 
        return 0;
 }
@@ -172,7 +198,11 @@ int ls1x_eth_mux_init(struct platform_device *pdev, void *priv)
 static struct plat_stmmacenet_data ls1x_eth0_pdata = {
        .bus_id         = 0,
        .phy_addr       = -1,
+#if defined(CONFIG_LOONGSON1_LS1B)
        .interface      = PHY_INTERFACE_MODE_MII,
+#elif defined(CONFIG_LOONGSON1_LS1C)
+       .interface      = PHY_INTERFACE_MODE_RMII,
+#endif
        .mdio_bus_data  = &ls1x_mdio_bus_data,
        .dma_cfg        = &ls1x_eth_dma_cfg,
        .has_gmac       = 1,
@@ -203,6 +233,7 @@ struct platform_device ls1x_eth0_pdev = {
        },
 };
 
+#ifdef CONFIG_LOONGSON1_LS1B
 static struct plat_stmmacenet_data ls1x_eth1_pdata = {
        .bus_id         = 1,
        .phy_addr       = -1,
@@ -236,6 +267,7 @@ struct platform_device ls1x_eth1_pdev = {
                .platform_data = &ls1x_eth1_pdata,
        },
 };
+#endif /* CONFIG_LOONGSON1_LS1B */
 
 /* GPIO */
 static struct resource ls1x_gpio0_resources[] = {
index 62f41afee241e7a4df305725bac9659c0a796bea..1640744288ee020df7200b5f01165749075bfe67 100644 (file)
@@ -22,7 +22,11 @@ const char *get_system_type(void)
 
        switch (processor_id & PRID_REV_MASK) {
        case PRID_REV_LOONGSON1B:
+#if defined(CONFIG_LOONGSON1_LS1B)
                return "LOONGSON LS1B";
+#elif defined(CONFIG_LOONGSON1_LS1C)
+               return "LOONGSON LS1C";
+#endif
        default:
                return "LOONGSON (unknown)";
        }
diff --git a/arch/mips/loongson32/ls1c/Makefile b/arch/mips/loongson32/ls1c/Makefile
new file mode 100644 (file)
index 0000000..a92c6cd
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for loongson1C based machines.
+#
+
+obj-y += board.o
diff --git a/arch/mips/loongson32/ls1c/board.c b/arch/mips/loongson32/ls1c/board.c
new file mode 100644 (file)
index 0000000..a96bed5
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 Yang Ling <gnaygnil@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.
+ */
+
+#include <platform.h>
+
+static struct platform_device *ls1c_platform_devices[] __initdata = {
+       &ls1x_uart_pdev,
+       &ls1x_eth0_pdev,
+       &ls1x_rtc_pdev,
+};
+
+static int __init ls1c_platform_init(void)
+{
+       ls1x_serial_set_uartclk(&ls1x_uart_pdev);
+       ls1x_rtc_set_extclk(&ls1x_rtc_pdev);
+
+       return platform_add_devices(ls1c_platform_devices,
+                                  ARRAY_SIZE(ls1c_platform_devices));
+}
+
+arch_initcall(ls1c_platform_init);
index 05b1d7cf9514c71422c587ddd4955a2a4992623e..0e45b061e514153a397b9d953986a6cc077f721e 100644 (file)
@@ -294,6 +294,8 @@ void octeon_cache_init(void)
        flush_data_cache_page           = octeon_flush_data_cache_page;
        flush_icache_range              = octeon_flush_icache_range;
        local_flush_icache_range        = local_octeon_flush_icache_range;
+       __flush_icache_user_range       = octeon_flush_icache_range;
+       __local_flush_icache_user_range = local_octeon_flush_icache_range;
 
        __flush_kernel_vmap_range       = octeon_flush_kernel_vmap_range;
 
index 135ec313c1f6594b31fbda33a7f8fcfc05735e95..21e4e662c1fa6c29597a37ab837643d68141a88b 100644 (file)
@@ -325,6 +325,8 @@ void r3k_cache_init(void)
        flush_cache_page = r3k_flush_cache_page;
        flush_icache_range = r3k_flush_icache_range;
        local_flush_icache_range = r3k_flush_icache_range;
+       __flush_icache_user_range = r3k_flush_icache_range;
+       __local_flush_icache_user_range = r3k_flush_icache_range;
 
        __flush_kernel_vmap_range = r3k_flush_kernel_vmap_range;
 
index fa7d8d3790bfc960bc7d4b358e9fb1a5120c04e2..88cfaf81c958733397a08ccb79d8c4c021f90580 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/sched.h>
 #include <linux/smp.h>
 #include <linux/mm.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/bitops.h>
 
 #include <asm/bcache.h>
@@ -722,11 +722,13 @@ struct flush_icache_range_args {
        unsigned long start;
        unsigned long end;
        unsigned int type;
+       bool user;
 };
 
 static inline void __local_r4k_flush_icache_range(unsigned long start,
                                                  unsigned long end,
-                                                 unsigned int type)
+                                                 unsigned int type,
+                                                 bool user)
 {
        if (!cpu_has_ic_fills_f_dc) {
                if (type == R4K_INDEX ||
@@ -734,7 +736,10 @@ static inline void __local_r4k_flush_icache_range(unsigned long start,
                        r4k_blast_dcache();
                } else {
                        R4600_HIT_CACHEOP_WAR_IMPL;
-                       protected_blast_dcache_range(start, end);
+                       if (user)
+                               protected_blast_dcache_range(start, end);
+                       else
+                               blast_dcache_range(start, end);
                }
        }
 
@@ -748,27 +753,25 @@ static inline void __local_r4k_flush_icache_range(unsigned long start,
                        break;
 
                default:
-                       protected_blast_icache_range(start, end);
+                       if (user)
+                               protected_blast_icache_range(start, end);
+                       else
+                               blast_icache_range(start, end);
                        break;
                }
        }
-#ifdef CONFIG_EVA
-       /*
-        * Due to all possible segment mappings, there might cache aliases
-        * caused by the bootloader being in non-EVA mode, and the CPU switching
-        * to EVA during early kernel init. It's best to flush the scache
-        * to avoid having secondary cores fetching stale data and lead to
-        * kernel crashes.
-        */
-       bc_wback_inv(start, (end - start));
-       __sync();
-#endif
 }
 
 static inline void local_r4k_flush_icache_range(unsigned long start,
                                                unsigned long end)
 {
-       __local_r4k_flush_icache_range(start, end, R4K_HIT | R4K_INDEX);
+       __local_r4k_flush_icache_range(start, end, R4K_HIT | R4K_INDEX, false);
+}
+
+static inline void local_r4k_flush_icache_user_range(unsigned long start,
+                                                    unsigned long end)
+{
+       __local_r4k_flush_icache_range(start, end, R4K_HIT | R4K_INDEX, true);
 }
 
 static inline void local_r4k_flush_icache_range_ipi(void *args)
@@ -777,11 +780,13 @@ static inline void local_r4k_flush_icache_range_ipi(void *args)
        unsigned long start = fir_args->start;
        unsigned long end = fir_args->end;
        unsigned int type = fir_args->type;
+       bool user = fir_args->user;
 
-       __local_r4k_flush_icache_range(start, end, type);
+       __local_r4k_flush_icache_range(start, end, type, user);
 }
 
-static void r4k_flush_icache_range(unsigned long start, unsigned long end)
+static void __r4k_flush_icache_range(unsigned long start, unsigned long end,
+                                    bool user)
 {
        struct flush_icache_range_args args;
        unsigned long size, cache_size;
@@ -789,6 +794,7 @@ static void r4k_flush_icache_range(unsigned long start, unsigned long end)
        args.start = start;
        args.end = end;
        args.type = R4K_HIT | R4K_INDEX;
+       args.user = user;
 
        /*
         * Indexed cache ops require an SMP call.
@@ -814,6 +820,16 @@ static void r4k_flush_icache_range(unsigned long start, unsigned long end)
        instruction_hazard();
 }
 
+static void r4k_flush_icache_range(unsigned long start, unsigned long end)
+{
+       return __r4k_flush_icache_range(start, end, false);
+}
+
+static void r4k_flush_icache_user_range(unsigned long start, unsigned long end)
+{
+       return __r4k_flush_icache_range(start, end, true);
+}
+
 #if defined(CONFIG_DMA_NONCOHERENT) || defined(CONFIG_DMA_MAYBE_COHERENT)
 
 static void r4k_dma_cache_wback_inv(unsigned long addr, unsigned long size)
@@ -1915,9 +1931,16 @@ void r4k_cache_init(void)
        flush_data_cache_page   = r4k_flush_data_cache_page;
        flush_icache_range      = r4k_flush_icache_range;
        local_flush_icache_range        = local_r4k_flush_icache_range;
+       __flush_icache_user_range       = r4k_flush_icache_user_range;
+       __local_flush_icache_user_range = local_r4k_flush_icache_user_range;
 
 #if defined(CONFIG_DMA_NONCOHERENT) || defined(CONFIG_DMA_MAYBE_COHERENT)
-       if (coherentio) {
+# if defined(CONFIG_DMA_PERDEV_COHERENT)
+       if (0) {
+# else
+       if ((coherentio == IO_COHERENCE_ENABLED) ||
+           ((coherentio == IO_COHERENCE_DEFAULT) && hw_coherentio)) {
+# endif
                _dma_cache_wback_inv    = (void *)cache_noop;
                _dma_cache_wback        = (void *)cache_noop;
                _dma_cache_inv          = (void *)cache_noop;
index 596e18458e041cf74d2cdf2974fcc701144591c0..5c282583edf16a5c95a638707199e80be3cd734b 100644 (file)
@@ -411,6 +411,9 @@ void tx39_cache_init(void)
                break;
        }
 
+       __flush_icache_user_range = flush_icache_range;
+       __local_flush_icache_user_range = local_flush_icache_range;
+
        current_cpu_data.icache.waysize = icache_size / current_cpu_data.icache.ways;
        current_cpu_data.dcache.waysize = dcache_size / current_cpu_data.dcache.ways;
 
index bf04c6c479a4e6ec3f99a90a8d14c093c63d5b6f..6db3413472023dbf586c433c2808b9c62ca67a8f 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/fcntl.h>
 #include <linux/kernel.h>
 #include <linux/linkage.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/sched.h>
 #include <linux/syscalls.h>
 #include <linux/mm.h>
@@ -33,6 +33,10 @@ void (*flush_icache_range)(unsigned long start, unsigned long end);
 EXPORT_SYMBOL_GPL(flush_icache_range);
 void (*local_flush_icache_range)(unsigned long start, unsigned long end);
 EXPORT_SYMBOL_GPL(local_flush_icache_range);
+void (*__flush_icache_user_range)(unsigned long start, unsigned long end);
+EXPORT_SYMBOL_GPL(__flush_icache_user_range);
+void (*__local_flush_icache_user_range)(unsigned long start, unsigned long end);
+EXPORT_SYMBOL_GPL(__local_flush_icache_user_range);
 
 void (*__flush_cache_vmap)(void);
 void (*__flush_cache_vunmap)(void);
@@ -74,7 +78,7 @@ SYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, bytes,
        if (!access_ok(VERIFY_WRITE, (void __user *) addr, bytes))
                return -EFAULT;
 
-       flush_icache_range(addr, addr + bytes);
+       __flush_icache_user_range(addr, addr + bytes);
 
        return 0;
 }
index b2eadd6fa9a1ed06167231eb9ab3632203b52d65..46d5696c4f276a7cdd729057fb4ee7044146d9e3 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/types.h>
 #include <linux/dma-mapping.h>
 #include <linux/mm.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/scatterlist.h>
 #include <linux/string.h>
 #include <linux/gfp.h>
 
 #include <dma-coherence.h>
 
-#ifdef CONFIG_DMA_MAYBE_COHERENT
-int coherentio = 0;    /* User defined DMA coherency from command line. */
+#if defined(CONFIG_DMA_MAYBE_COHERENT) && !defined(CONFIG_DMA_PERDEV_COHERENT)
+/* User defined DMA coherency from command line. */
+enum coherent_io_user_state coherentio = IO_COHERENCE_DEFAULT;
 EXPORT_SYMBOL_GPL(coherentio);
 int hw_coherentio = 0; /* Actual hardware supported DMA coherency setting. */
 
 static int __init setcoherentio(char *str)
 {
-       coherentio = 1;
+       coherentio = IO_COHERENCE_ENABLED;
        pr_info("Hardware DMA cache coherency (command line)\n");
        return 0;
 }
@@ -39,7 +40,7 @@ early_param("coherentio", setcoherentio);
 
 static int __init setnocoherentio(char *str)
 {
-       coherentio = 0;
+       coherentio = IO_COHERENCE_DISABLED;
        pr_info("Software DMA cache coherency (command line)\n");
        return 0;
 }
@@ -160,8 +161,7 @@ static void *mips_dma_alloc_coherent(struct device *dev, size_t size,
        *dma_handle = plat_map_dma_mem(dev, ret, size);
        if (!plat_device_is_coherent(dev)) {
                dma_cache_wback_inv((unsigned long) ret, size);
-               if (!hw_coherentio)
-                       ret = UNCAC_ADDR(ret);
+               ret = UNCAC_ADDR(ret);
        }
 
        return ret;
@@ -189,7 +189,7 @@ static void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr,
 
        plat_unmap_dma_mem(dev, dma_handle, size, DMA_BIDIRECTIONAL);
 
-       if (!plat_device_is_coherent(dev) && !hw_coherentio)
+       if (!plat_device_is_coherent(dev))
                addr = CAC_ADDR(addr);
 
        page = virt_to_page((void *) addr);
@@ -209,7 +209,7 @@ static int mips_dma_mmap(struct device *dev, struct vm_area_struct *vma,
        unsigned long pfn;
        int ret = -ENXIO;
 
-       if (!plat_device_is_coherent(dev) && !hw_coherentio)
+       if (!plat_device_is_coherent(dev))
                addr = CAC_ADDR(addr);
 
        pfn = page_to_pfn(virt_to_page((void *)addr));
index 9d25d2ba4b9ea8b0157bea763fa314a6556227f6..e474fa2efed49fbe30467fc38c268717905684ac 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (C) 1997, 99, 2001 - 2004 Ralf Baechle <ralf@linux-mips.org>
  */
-#include <linux/module.h>
+#include <linux/extable.h>
 #include <linux/spinlock.h>
 #include <asm/branch.h>
 #include <asm/uaccess.h>
index 9560ad73112093a2b3c1573b4c54e5993e97328c..d56a855828c2bbc1235873ab7fd03c3836fb7b4a 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/mman.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
-#include <linux/module.h>
 #include <linux/kprobes.h>
 #include <linux/perf_event.h>
 #include <linux/uaccess.h>
index 42d124fb6474477c896e4618713e5b6d65d6e8f3..d8c3c159289a2953b8ae495a1da34d27d095b161 100644 (file)
@@ -287,7 +287,7 @@ slow_irqon:
        pages += nr;
 
        ret = get_user_pages_unlocked(start, (end - start) >> PAGE_SHIFT,
-                                     write, 0, pages);
+                                     pages, write ? FOLL_WRITE : 0);
 
        /* Have to be a bit careful with return values */
        if (nr > 0) {
index d7258a103439d8316b527d64569955339489fe4c..f13f51003bd83c7db13c8e7f298b85c14e4a962f 100644 (file)
@@ -1,5 +1,6 @@
 #include <linux/compiler.h>
-#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/export.h>
 #include <linux/highmem.h>
 #include <linux/sched.h>
 #include <linux/smp.h>
index 72f7478ee068408d9398813c9f75e21b98c80a3b..3a6edecc3f385e4bd897750aa66488623f60411e 100644 (file)
@@ -10,7 +10,7 @@
  */
 #include <linux/bug.h>
 #include <linux/init.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/smp.h>
index 8d5008cbdc0f0a580d1fbc2c65f6f8741cbdc5eb..1f189627440f235b0876966b21562e55bc665323 100644 (file)
@@ -6,7 +6,7 @@
  * (C) Copyright 1995 1996 Linus Torvalds
  * (C) Copyright 2001, 2002 Ralf Baechle
  */
-#include <linux/module.h>
+#include <linux/export.h>
 #include <asm/addrspace.h>
 #include <asm/byteorder.h>
 #include <linux/sched.h>
index 353037699512ca5515b11ce8fb2c808eb6386c78..d08ea3ff0f53345e7501dd168f32c2177976f6ee 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/errno.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/personality.h>
 #include <linux/random.h>
 #include <linux/sched.h>
index c41953ca6605ca347db0bbe71689fb791ef51410..6f804f5960abeff77cdbcd2cecf48a1acf989e7c 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/sched.h>
 #include <linux/smp.h>
 #include <linux/mm.h>
-#include <linux/module.h>
 #include <linux/proc_fs.h>
 
 #include <asm/bugs.h>
index e8b335c162958030adb7a201f6a92a8b05018153..bba9c1484b41e1bc8c124b3a2c50e53eb27e032c 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/smp.h>
 #include <linux/mm.h>
 #include <linux/hugetlb.h>
-#include <linux/module.h>
+#include <linux/export.h>
 
 #include <asm/cpu.h>
 #include <asm/cpu-type.h>
@@ -67,8 +67,11 @@ void local_flush_tlb_all(void)
 
        entry = read_c0_wired();
 
-       /* Blast 'em all away. */
-       if (cpu_has_tlbinv) {
+       /*
+        * Blast 'em all away.
+        * If there are any wired entries, fall back to iterating
+        */
+       if (cpu_has_tlbinv && !entry) {
                if (current_cpu_data.tlbsizevtlb) {
                        write_c0_index(0);
                        mtc0_tlbw_hazard();
index 47a22889285f33db32b83c542e643b742133681e..4822943100f303c0fe08c7d7a3514ea7e0a8b927 100644 (file)
@@ -17,18 +17,3 @@ void __init device_tree_init(void)
 {
        unflatten_and_copy_device_tree();
 }
-
-static const struct of_device_id bus_ids[] __initconst = {
-       { .compatible = "simple-bus", },
-       { .compatible = "isa", },
-       {},
-};
-
-static int __init publish_devices(void)
-{
-       if (!of_have_populated_dt())
-               return 0;
-
-       return of_platform_bus_probe(NULL, bus_ids, NULL);
-}
-device_initcall(publish_devices);
index 151f4882ec8ac62d6cd8993a38d353d352bdd77e..c398582c316fcc9a8b01f567052ac4bb2ef18c26 100644 (file)
 #include <linux/libfdt.h>
 #include <linux/of_fdt.h>
 #include <linux/sizes.h>
+#include <asm/addrspace.h>
 #include <asm/bootinfo.h>
 #include <asm/fw/fw.h>
+#include <asm/mips-boards/generic.h>
+#include <asm/mips-boards/malta.h>
+#include <asm/mips-cm.h>
 #include <asm/page.h>
 
+#define ROCIT_REG_BASE                 0x1f403000
+#define ROCIT_CONFIG_GEN1              (ROCIT_REG_BASE + 0x04)
+#define  ROCIT_CONFIG_GEN1_MEMMAP_SHIFT        8
+#define  ROCIT_CONFIG_GEN1_MEMMAP_MASK (0xf << 8)
+
 static unsigned char fdt_buf[16 << 10] __initdata;
 
 /* determined physical memory size, not overridden by command line args         */
 extern unsigned long physical_memsize;
 
-#define MAX_MEM_ARRAY_ENTRIES 1
+enum mem_map {
+       MEM_MAP_V1 = 0,
+       MEM_MAP_V2,
+};
+
+#define MAX_MEM_ARRAY_ENTRIES 2
 
-static unsigned __init gen_fdt_mem_array(__be32 *mem_array, unsigned long size)
+static __init int malta_scon(void)
+{
+       int scon = MIPS_REVISION_SCONID;
+
+       if (scon != MIPS_REVISION_SCON_OTHER)
+               return scon;
+
+       switch (MIPS_REVISION_CORID) {
+       case MIPS_REVISION_CORID_QED_RM5261:
+       case MIPS_REVISION_CORID_CORE_LV:
+       case MIPS_REVISION_CORID_CORE_FPGA:
+       case MIPS_REVISION_CORID_CORE_FPGAR2:
+               return MIPS_REVISION_SCON_GT64120;
+
+       case MIPS_REVISION_CORID_CORE_EMUL_BON:
+       case MIPS_REVISION_CORID_BONITO64:
+       case MIPS_REVISION_CORID_CORE_20K:
+               return MIPS_REVISION_SCON_BONITO;
+
+       case MIPS_REVISION_CORID_CORE_MSC:
+       case MIPS_REVISION_CORID_CORE_FPGA2:
+       case MIPS_REVISION_CORID_CORE_24K:
+               return MIPS_REVISION_SCON_SOCIT;
+
+       case MIPS_REVISION_CORID_CORE_FPGA3:
+       case MIPS_REVISION_CORID_CORE_FPGA4:
+       case MIPS_REVISION_CORID_CORE_FPGA5:
+       case MIPS_REVISION_CORID_CORE_EMUL_MSC:
+       default:
+               return MIPS_REVISION_SCON_ROCIT;
+       }
+}
+
+static unsigned __init gen_fdt_mem_array(__be32 *mem_array, unsigned long size,
+                                        enum mem_map map)
 {
        unsigned long size_preio;
        unsigned entries;
@@ -39,11 +87,47 @@ static unsigned __init gen_fdt_mem_array(__be32 *mem_array, unsigned long size)
                 * DDR but limits it to 2GB.
                 */
                mem_array[1] = cpu_to_be32(size);
+               goto done;
+       }
+
+       size_preio = min_t(unsigned long, size, SZ_256M);
+       mem_array[1] = cpu_to_be32(size_preio);
+       size -= size_preio;
+       if (!size)
+               goto done;
+
+       if (map == MEM_MAP_V2) {
+               /*
+                * We have a flat 32 bit physical memory map with DDR filling
+                * all 4GB of the memory map, apart from the I/O region which
+                * obscures 256MB from 0x10000000-0x1fffffff.
+                *
+                * Therefore we discard the 256MB behind the I/O region.
+                */
+               if (size <= SZ_256M)
+                       goto done;
+               size -= SZ_256M;
+
+               /* Make use of the memory following the I/O region */
+               entries++;
+               mem_array[2] = cpu_to_be32(PHYS_OFFSET + SZ_512M);
+               mem_array[3] = cpu_to_be32(size);
        } else {
-               size_preio = min_t(unsigned long, size, SZ_256M);
-               mem_array[1] = cpu_to_be32(size_preio);
+               /*
+                * We have a 32 bit physical memory map with a 2GB DDR region
+                * aliased in the upper & lower halves of it. The I/O region
+                * obscures 256MB from 0x10000000-0x1fffffff in the low alias
+                * but the DDR it obscures is accessible via the high alias.
+                *
+                * Simply access everything beyond the lowest 256MB of DDR using
+                * the high alias.
+                */
+               entries++;
+               mem_array[2] = cpu_to_be32(PHYS_OFFSET + SZ_2G + SZ_256M);
+               mem_array[3] = cpu_to_be32(size);
        }
 
+done:
        BUG_ON(entries > MAX_MEM_ARRAY_ENTRIES);
        return entries;
 }
@@ -54,6 +138,8 @@ static void __init append_memory(void *fdt, int root_off)
        unsigned long memsize;
        unsigned mem_entries;
        int i, err, mem_off;
+       enum mem_map mem_map;
+       u32 config;
        char *var, param_name[10], *var_names[] = {
                "ememsize", "memsize",
        };
@@ -106,6 +192,20 @@ static void __init append_memory(void *fdt, int root_off)
        /* if the user says there's more RAM than we thought, believe them */
        physical_memsize = max_t(unsigned long, physical_memsize, memsize);
 
+       /* detect the memory map in use */
+       if (malta_scon() == MIPS_REVISION_SCON_ROCIT) {
+               /* ROCit has a register indicating the memory map in use */
+               config = readl((void __iomem *)CKSEG1ADDR(ROCIT_CONFIG_GEN1));
+               mem_map = config & ROCIT_CONFIG_GEN1_MEMMAP_MASK;
+               mem_map >>= ROCIT_CONFIG_GEN1_MEMMAP_SHIFT;
+       } else {
+               /* if not using ROCit, presume the v1 memory map */
+               mem_map = MEM_MAP_V1;
+       }
+       if (mem_map > MEM_MAP_V2)
+               panic("Unsupported physical memory map v%u detected",
+                     (unsigned int)mem_map);
+
        /* append memory to the DT */
        mem_off = fdt_add_subnode(fdt, root_off, "memory");
        if (mem_off < 0)
@@ -115,19 +215,93 @@ static void __init append_memory(void *fdt, int root_off)
        if (err)
                panic("Unable to set memory node device_type: %d", err);
 
-       mem_entries = gen_fdt_mem_array(mem_array, physical_memsize);
+       mem_entries = gen_fdt_mem_array(mem_array, physical_memsize, mem_map);
        err = fdt_setprop(fdt, mem_off, "reg", mem_array,
                          mem_entries * 2 * sizeof(mem_array[0]));
        if (err)
                panic("Unable to set memory regs property: %d", err);
 
-       mem_entries = gen_fdt_mem_array(mem_array, memsize);
+       mem_entries = gen_fdt_mem_array(mem_array, memsize, mem_map);
        err = fdt_setprop(fdt, mem_off, "linux,usable-memory", mem_array,
                          mem_entries * 2 * sizeof(mem_array[0]));
        if (err)
                panic("Unable to set linux,usable-memory property: %d", err);
 }
 
+static void __init remove_gic(void *fdt)
+{
+       int err, gic_off, i8259_off, cpu_off;
+       void __iomem *biu_base;
+       uint32_t cpu_phandle, sc_cfg;
+
+       /* if we have a CM which reports a GIC is present, leave the DT alone */
+       err = mips_cm_probe();
+       if (!err && (read_gcr_gic_status() & CM_GCR_GIC_STATUS_GICEX_MSK))
+               return;
+
+       if (malta_scon() == MIPS_REVISION_SCON_ROCIT) {
+               /*
+                * On systems using the RocIT system controller a GIC may be
+                * present without a CM. Detect whether that is the case.
+                */
+               biu_base = ioremap_nocache(MSC01_BIU_REG_BASE,
+                               MSC01_BIU_ADDRSPACE_SZ);
+               sc_cfg = __raw_readl(biu_base + MSC01_SC_CFG_OFS);
+               if (sc_cfg & MSC01_SC_CFG_GICPRES_MSK) {
+                       /* enable the GIC at the system controller level */
+                       sc_cfg |= BIT(MSC01_SC_CFG_GICENA_SHF);
+                       __raw_writel(sc_cfg, biu_base + MSC01_SC_CFG_OFS);
+                       return;
+               }
+       }
+
+       gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic");
+       if (gic_off < 0) {
+               pr_warn("malta-dtshim: unable to find DT GIC node: %d\n",
+                       gic_off);
+               return;
+       }
+
+       err = fdt_nop_node(fdt, gic_off);
+       if (err)
+               pr_warn("malta-dtshim: unable to nop GIC node\n");
+
+       i8259_off = fdt_node_offset_by_compatible(fdt, -1, "intel,i8259");
+       if (i8259_off < 0) {
+               pr_warn("malta-dtshim: unable to find DT i8259 node: %d\n",
+                       i8259_off);
+               return;
+       }
+
+       cpu_off = fdt_node_offset_by_compatible(fdt, -1,
+                       "mti,cpu-interrupt-controller");
+       if (cpu_off < 0) {
+               pr_warn("malta-dtshim: unable to find CPU intc node: %d\n",
+                       cpu_off);
+               return;
+       }
+
+       cpu_phandle = fdt_get_phandle(fdt, cpu_off);
+       if (!cpu_phandle) {
+               pr_warn("malta-dtshim: unable to get CPU intc phandle\n");
+               return;
+       }
+
+       err = fdt_setprop_u32(fdt, i8259_off, "interrupt-parent", cpu_phandle);
+       if (err) {
+               pr_warn("malta-dtshim: unable to set i8259 interrupt-parent: %d\n",
+                       err);
+               return;
+       }
+
+       err = fdt_setprop_u32(fdt, i8259_off, "interrupts", 2);
+       if (err) {
+               pr_warn("malta-dtshim: unable to set i8259 interrupts: %d\n",
+                       err);
+               return;
+       }
+}
+
 void __init *malta_dt_shim(void *fdt)
 {
        int root_off, len, err;
@@ -153,6 +327,7 @@ void __init *malta_dt_shim(void *fdt)
                return fdt;
 
        append_memory(fdt_buf, root_off);
+       remove_gic(fdt_buf);
 
        err = fdt_pack(fdt_buf);
        if (err)
index dc2c5214809d38703a7048babe7a16f83d77f28a..0f3b881a3190fdcb993ea01234a2c304b1511f18 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
+#include <linux/pci_regs.h>
 #include <linux/serial_core.h>
 
 #include <asm/cacheflush.h>
@@ -242,23 +243,19 @@ mips_pci_controller:
                          MSC01_PCI_SWAP_BYTESWAP << MSC01_PCI_SWAP_MEM_SHF |
                          MSC01_PCI_SWAP_BYTESWAP << MSC01_PCI_SWAP_BAR0_SHF);
 #endif
-#ifndef CONFIG_EVA
-               /* Fix up target memory mapping.  */
-               MSC_READ(MSC01_PCI_BAR0, mask);
-               MSC_WRITE(MSC01_PCI_P2SCMSKL, mask & MSC01_PCI_BAR0_SIZE_MSK);
-#else
+
                /*
                 * Setup the Malta max (2GB) memory for PCI DMA in host bridge
-                * in transparent addressing mode, starting from 0x80000000.
+                * in transparent addressing mode.
                 */
-               mask = PHYS_OFFSET | (1<<3);
+               mask = PHYS_OFFSET | PCI_BASE_ADDRESS_MEM_PREFETCH;
                MSC_WRITE(MSC01_PCI_BAR0, mask);
-
-               mask = PHYS_OFFSET;
                MSC_WRITE(MSC01_PCI_HEAD4, mask);
+
+               mask &= MSC01_PCI_BAR0_SIZE_MSK;
                MSC_WRITE(MSC01_PCI_P2SCMSKL, mask);
                MSC_WRITE(MSC01_PCI_P2SCMAPL, mask);
-#endif
+
                /* Don't handle target retries indefinitely.  */
                if ((data & MSC01_PCI_CFG_MAXRTRY_MSK) ==
                    MSC01_PCI_CFG_MAXRTRY_MSK)
index c6a6c7afddab41f00475bce99e53f861dc10db58..cb675ec6f283ee9d08071e9b112845bddd5c1594 100644 (file)
  */
 #include <linux/init.h>
 #include <linux/irq.h>
+#include <linux/irqchip.h>
 #include <linux/sched.h>
 #include <linux/smp.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/irqchip/mips-gic.h>
+#include <linux/of_irq.h>
 #include <linux/kernel_stat.h>
 #include <linux/kernel.h>
 #include <linux/random.h>
 #include <asm/setup.h>
 #include <asm/rtlx.h>
 
-static void __iomem *_msc01_biu_base;
-
-static DEFINE_RAW_SPINLOCK(mips_irq_lock);
-
 static inline int mips_pcibios_iack(void)
 {
        int irq;
@@ -85,49 +83,6 @@ static inline int mips_pcibios_iack(void)
        return irq;
 }
 
-static inline int get_int(void)
-{
-       unsigned long flags;
-       int irq;
-       raw_spin_lock_irqsave(&mips_irq_lock, flags);
-
-       irq = mips_pcibios_iack();
-
-       /*
-        * The only way we can decide if an interrupt is spurious
-        * is by checking the 8259 registers.  This needs a spinlock
-        * on an SMP system,  so leave it up to the generic code...
-        */
-
-       raw_spin_unlock_irqrestore(&mips_irq_lock, flags);
-
-       return irq;
-}
-
-static void malta_hw0_irqdispatch(void)
-{
-       int irq;
-
-       irq = get_int();
-       if (irq < 0) {
-               /* interrupt has already been cleared */
-               return;
-       }
-
-       do_IRQ(MALTA_INT_BASE + irq);
-
-#ifdef CONFIG_MIPS_VPE_APSP_API_MT
-       if (aprp_hook)
-               aprp_hook();
-#endif
-}
-
-static irqreturn_t i8259_handler(int irq, void *dev_id)
-{
-       malta_hw0_irqdispatch();
-       return IRQ_HANDLED;
-}
-
 static void corehi_irqdispatch(void)
 {
        unsigned int intedge, intsteer, pcicmd, pcibadaddr;
@@ -240,12 +195,6 @@ static struct irqaction irq_call = {
 };
 #endif /* CONFIG_MIPS_MT_SMP */
 
-static struct irqaction i8259irq = {
-       .handler = i8259_handler,
-       .name = "XT-PIC cascade",
-       .flags = IRQF_NO_THREAD,
-};
-
 static struct irqaction corehi_irqaction = {
        .handler = corehi_handler,
        .name = "CoreHi",
@@ -281,28 +230,10 @@ void __init arch_init_ipiirq(int irq, struct irqaction *action)
 
 void __init arch_init_irq(void)
 {
-       int corehi_irq, i8259_irq;
-
-       init_i8259_irqs();
+       int corehi_irq;
 
-       if (!cpu_has_veic)
-               mips_cpu_irq_init();
-
-       if (mips_cm_present()) {
-               write_gcr_gic_base(GIC_BASE_ADDR | CM_GCR_GIC_BASE_GICEN_MSK);
-               gic_present = 1;
-       } else {
-               if (mips_revision_sconid == MIPS_REVISION_SCON_ROCIT) {
-                       _msc01_biu_base = ioremap_nocache(MSC01_BIU_REG_BASE,
-                                               MSC01_BIU_ADDRSPACE_SZ);
-                       gic_present =
-                         (__raw_readl(_msc01_biu_base + MSC01_SC_CFG_OFS) &
-                          MSC01_SC_CFG_GICPRES_MSK) >>
-                         MSC01_SC_CFG_GICPRES_SHF;
-               }
-       }
-       if (gic_present)
-               pr_debug("GIC present\n");
+       i8259_set_poll(mips_pcibios_iack);
+       irqchip_init();
 
        switch (mips_revision_sconid) {
        case MIPS_REVISION_SCON_SOCIT:
@@ -330,18 +261,6 @@ void __init arch_init_irq(void)
        }
 
        if (gic_present) {
-               int i;
-
-               gic_init(GIC_BASE_ADDR, GIC_ADDRSPACE_SZ, MIPSCPU_INT_GIC,
-                        MIPS_GIC_IRQ_BASE);
-               if (!mips_cm_present()) {
-                       /* Enable the GIC */
-                       i = __raw_readl(_msc01_biu_base + MSC01_SC_CFG_OFS);
-                       __raw_writel(i | (0x1 << MSC01_SC_CFG_GICENA_SHF),
-                                _msc01_biu_base + MSC01_SC_CFG_OFS);
-                       pr_debug("GIC Enabled\n");
-               }
-               i8259_irq = MIPS_GIC_IRQ_BASE + GIC_INT_I8259A;
                corehi_irq = MIPS_CPU_IRQ_BASE + MIPSCPU_INT_COREHI;
        } else {
 #if defined(CONFIG_MIPS_MT_SMP)
@@ -361,33 +280,13 @@ void __init arch_init_irq(void)
                arch_init_ipiirq(cpu_ipi_call_irq, &irq_call);
 #endif
                if (cpu_has_veic) {
-                       set_vi_handler(MSC01E_INT_I8259A,
-                                      malta_hw0_irqdispatch);
                        set_vi_handler(MSC01E_INT_COREHI,
                                       corehi_irqdispatch);
-                       i8259_irq = MSC01E_INT_BASE + MSC01E_INT_I8259A;
                        corehi_irq = MSC01E_INT_BASE + MSC01E_INT_COREHI;
                } else {
-                       i8259_irq = MIPS_CPU_IRQ_BASE + MIPSCPU_INT_I8259A;
                        corehi_irq = MIPS_CPU_IRQ_BASE + MIPSCPU_INT_COREHI;
                }
        }
 
-       setup_irq(i8259_irq, &i8259irq);
        setup_irq(corehi_irq, &corehi_irqaction);
 }
-
-void malta_be_init(void)
-{
-       /* Could change CM error mask register. */
-}
-
-int malta_be_handler(struct pt_regs *regs, int is_fixup)
-{
-       /* This duplicates the handling in do_be which seems wrong */
-       int retval = is_fixup ? MIPS_BE_FIXUP : MIPS_BE_FATAL;
-
-       mips_cm_error_report();
-
-       return retval;
-}
index e1dd1c1d3fdeed9f5214dc18ec4cb7f2c5593279..516e1233d771cb3cd87cde8e73ef6f4d296e861b 100644 (file)
  */
 #include <linux/init.h>
 #include <linux/serial_8250.h>
-#include <linux/mc146818rtc.h>
 #include <linux/module.h>
 #include <linux/irq.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
 #include <linux/platform_device.h>
 #include <asm/mips-boards/maltaint.h>
-#include <mtd/mtd-abi.h>
 
 #define SMC_PORT(base, int)                                            \
 {                                                                      \
@@ -68,80 +64,13 @@ static struct platform_device malta_uart8250_device = {
        },
 };
 
-struct resource malta_rtc_resources[] = {
-       {
-               .start  = RTC_PORT(0),
-               .end    = RTC_PORT(7),
-               .flags  = IORESOURCE_IO,
-       }, {
-               .start  = RTC_IRQ,
-               .end    = RTC_IRQ,
-               .flags  = IORESOURCE_IRQ,
-       }
-};
-
-static struct platform_device malta_rtc_device = {
-       .name           = "rtc_cmos",
-       .id             = -1,
-       .resource       = malta_rtc_resources,
-       .num_resources  = ARRAY_SIZE(malta_rtc_resources),
-};
-
-static struct mtd_partition malta_mtd_partitions[] = {
-       {
-               .name =         "YAMON",
-               .offset =       0x0,
-               .size =         0x100000,
-               .mask_flags =   MTD_WRITEABLE
-       }, {
-               .name =         "User FS",
-               .offset =       0x100000,
-               .size =         0x2e0000
-       }, {
-               .name =         "Board Config",
-               .offset =       0x3e0000,
-               .size =         0x020000,
-               .mask_flags =   MTD_WRITEABLE
-       }
-};
-
-static struct physmap_flash_data malta_flash_data = {
-       .width          = 4,
-       .nr_parts       = ARRAY_SIZE(malta_mtd_partitions),
-       .parts          = malta_mtd_partitions
-};
-
-static struct resource malta_flash_resource = {
-       .start          = 0x1e000000,
-       .end            = 0x1e3fffff,
-       .flags          = IORESOURCE_MEM
-};
-
-static struct platform_device malta_flash_device = {
-       .name           = "physmap-flash",
-       .id             = 0,
-       .dev            = {
-               .platform_data  = &malta_flash_data,
-       },
-       .num_resources  = 1,
-       .resource       = &malta_flash_resource,
-};
-
 static struct platform_device *malta_devices[] __initdata = {
        &malta_uart8250_device,
-       &malta_rtc_device,
-       &malta_flash_device,
 };
 
 static int __init malta_add_devices(void)
 {
-       int err;
-
-       err = platform_add_devices(malta_devices, ARRAY_SIZE(malta_devices));
-       if (err)
-               return err;
-
-       return 0;
+       return platform_add_devices(malta_devices, ARRAY_SIZE(malta_devices));
 }
 
 device_initcall(malta_add_devices);
index 2fd2cc2c5034fbf1ddd59ffec163fab76187aa4a..dd6f62ad4417225cf92b63bd6ff7a886c5f01c97 100644 (file)
@@ -8,38 +8,21 @@
  */
 #include <linux/io.h>
 #include <linux/pm.h>
+#include <linux/reboot.h>
 
 #include <asm/reboot.h>
 #include <asm/mach-malta/malta-pm.h>
 
-#define SOFTRES_REG    0x1f000500
-#define GORESET                0x42
-
-static void mips_machine_restart(char *command)
-{
-       unsigned int __iomem *softres_reg =
-               ioremap(SOFTRES_REG, sizeof(unsigned int));
-
-       __raw_writel(GORESET, softres_reg);
-}
-
-static void mips_machine_halt(void)
-{
-       while (true);
-}
-
 static void mips_machine_power_off(void)
 {
        mips_pm_suspend(PIIX4_FUNC3IO_PMCNTRL_SUS_TYP_SOFF);
 
        pr_info("Failed to power down, resetting\n");
-       mips_machine_restart(NULL);
+       machine_restart(NULL);
 }
 
 static int __init mips_reboot_setup(void)
 {
-       _machine_restart = mips_machine_restart;
-       _machine_halt = mips_machine_halt;
        pm_power_off = mips_machine_power_off;
 
        return 0;
index 7e7364b0501edc33f1746962dc9b3796000d3c69..a01d5debfcaf5578a30b6114721db82a1c392e19 100644 (file)
@@ -42,9 +42,6 @@
 #define ROCIT_CONFIG_GEN0              0x1f403000
 #define  ROCIT_CONFIG_GEN0_PCI_IOCU    BIT(7)
 
-extern void malta_be_init(void);
-extern int malta_be_handler(struct pt_regs *regs, int is_fixup);
-
 static struct resource standard_io_resources[] = {
        {
                .name = "dma1",
@@ -154,12 +151,12 @@ static void __init plat_setup_iocoherency(void)
         * coherency instead.
         */
        if (plat_enable_iocoherency()) {
-               if (coherentio == 0)
+               if (coherentio == IO_COHERENCE_DISABLED)
                        pr_info("Hardware DMA cache coherency disabled\n");
                else
                        pr_info("Hardware DMA cache coherency enabled\n");
        } else {
-               if (coherentio == 1)
+               if (coherentio == IO_COHERENCE_ENABLED)
                        pr_info("Hardware DMA cache coherency unsupported, but enabled from command line!\n");
                else
                        pr_info("Software DMA cache coherency enabled\n");
@@ -301,7 +298,4 @@ void __init plat_mem_setup(void)
 #if defined(CONFIG_VT) && defined(CONFIG_VGA_CONSOLE)
        screen_info_setup();
 #endif
-
-       board_be_init = malta_be_init;
-       board_be_handler = malta_be_handler;
 }
diff --git a/arch/mips/mti-sead3/Makefile b/arch/mips/mti-sead3/Makefile
deleted file mode 100644 (file)
index 7a584e0..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#
-# Carsten Langgaard, carstenl@mips.com
-# Copyright (C) 1999,2000 MIPS Technologies, Inc.  All rights reserved.
-#
-# Copyright (C) 2008 Wind River Systems, Inc.
-#   written by Ralf Baechle <ralf@linux-mips.org>
-#
-# Copyright (C) 2012 MIPS Technoligies, Inc.  All rights reserved.
-# Steven J. Hill <sjhill@mips.com>
-#
-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-$(CONFIG_EARLY_PRINTK)     += sead3-console.o
diff --git a/arch/mips/mti-sead3/Platform b/arch/mips/mti-sead3/Platform
deleted file mode 100644 (file)
index 3870924..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# MIPS SEAD-3 board
-#
-platform-$(CONFIG_MIPS_SEAD3)  += mti-sead3/
-cflags-$(CONFIG_MIPS_SEAD3)    += -I$(srctree)/arch/mips/include/asm/mach-sead3
-load-$(CONFIG_MIPS_SEAD3)      += 0xffffffff80100000
-all-$(CONFIG_MIPS_SEAD3)       := $(COMPRESSION_FNAME).srec
diff --git a/arch/mips/mti-sead3/sead3-console.c b/arch/mips/mti-sead3/sead3-console.c
deleted file mode 100644 (file)
index 031f47d..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
- */
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/serial_reg.h>
-#include <linux/io.h>
-
-#define SEAD_UART1_REGS_BASE   0xbf000800   /* ttyS1 = DB9 port */
-#define SEAD_UART0_REGS_BASE   0xbf000900   /* ttyS0 = USB port   */
-#define PORT(base_addr, offset) ((unsigned int __iomem *)(base_addr+(offset)*4))
-
-static char console_port = 1;
-
-static inline unsigned int serial_in(int offset, unsigned int base_addr)
-{
-       return __raw_readl(PORT(base_addr, offset)) & 0xff;
-}
-
-static inline void serial_out(int offset, int value, unsigned int base_addr)
-{
-       __raw_writel(value, PORT(base_addr, offset));
-}
-
-void __init fw_init_early_console(char port)
-{
-       console_port = port;
-}
-
-int prom_putchar(char c)
-{
-       unsigned int base_addr;
-
-       base_addr = console_port ? SEAD_UART1_REGS_BASE : SEAD_UART0_REGS_BASE;
-
-       while ((serial_in(UART_LSR, base_addr) & UART_LSR_THRE) == 0)
-               ;
-
-       serial_out(UART_TX, c, base_addr);
-
-       return 1;
-}
diff --git a/arch/mips/mti-sead3/sead3-display.c b/arch/mips/mti-sead3/sead3-display.c
deleted file mode 100644 (file)
index 9487599..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
- */
-#include <linux/timer.h>
-#include <linux/io.h>
-#include <asm/mips-boards/generic.h>
-
-static unsigned int display_count;
-static unsigned int max_display_count;
-
-#define LCD_DISPLAY_POS_BASE           0x1f000400
-#define DISPLAY_LCDINSTRUCTION         (0*2)
-#define DISPLAY_LCDDATA                        (1*2)
-#define DISPLAY_CPLDSTATUS             (2*2)
-#define DISPLAY_CPLDDATA               (3*2)
-#define LCD_SETDDRAM                   0x80
-#define LCD_IR_BF                      0x80
-
-const char display_string[] = "                      LINUX ON SEAD3               ";
-
-static void scroll_display_message(unsigned long data);
-static DEFINE_TIMER(mips_scroll_timer, scroll_display_message, HZ, 0);
-
-static void lcd_wait(unsigned int __iomem *display)
-{
-       /* Wait for CPLD state machine to become idle. */
-       do { } while (__raw_readl(display + DISPLAY_CPLDSTATUS) & 1);
-
-       do {
-               __raw_readl(display + DISPLAY_LCDINSTRUCTION);
-
-               /* Wait for CPLD state machine to become idle. */
-               do { } while (__raw_readl(display + DISPLAY_CPLDSTATUS) & 1);
-       } while (__raw_readl(display + DISPLAY_CPLDDATA) & LCD_IR_BF);
-}
-
-void mips_display_message(const char *str)
-{
-       static unsigned int __iomem *display;
-       char ch;
-       int i;
-
-       if (unlikely(display == NULL))
-               display = ioremap_nocache(LCD_DISPLAY_POS_BASE,
-                       (8 * sizeof(int)));
-
-       for (i = 0; i < 16; i++) {
-               if (*str)
-                       ch = *str++;
-               else
-                       ch = ' ';
-               lcd_wait(display);
-               __raw_writel((LCD_SETDDRAM | i),
-                       (display + DISPLAY_LCDINSTRUCTION));
-               lcd_wait(display);
-               __raw_writel(ch, display + DISPLAY_LCDDATA);
-       }
-}
-
-static void scroll_display_message(unsigned long data)
-{
-       mips_display_message(&display_string[display_count++]);
-       if (display_count == max_display_count)
-               display_count = 0;
-       mod_timer(&mips_scroll_timer, jiffies + HZ);
-}
-
-void mips_scroll_message(void)
-{
-       del_timer_sync(&mips_scroll_timer);
-       max_display_count = strlen(display_string) + 1 - 16;
-       mod_timer(&mips_scroll_timer, jiffies + 1);
-}
diff --git a/arch/mips/mti-sead3/sead3-init.c b/arch/mips/mti-sead3/sead3-init.c
deleted file mode 100644 (file)
index 3572ea3..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
- */
-#include <linux/init.h>
-#include <linux/io.h>
-
-#include <asm/bootinfo.h>
-#include <asm/cacheflush.h>
-#include <asm/traps.h>
-#include <asm/mips-boards/generic.h>
-#include <asm/fw/fw.h>
-
-extern char except_vec_nmi;
-extern char except_vec_ejtag_debug;
-
-#ifdef CONFIG_SERIAL_8250_CONSOLE
-static void __init console_config(void)
-{
-       char console_string[40];
-       int baud = 0;
-       char parity = '\0', bits = '\0', flow = '\0';
-       char *s;
-
-       if ((strstr(fw_getcmdline(), "console=")) == NULL) {
-               s = fw_getenv("modetty0");
-               if (s) {
-                       while (*s >= '0' && *s <= '9')
-                               baud = baud*10 + *s++ - '0';
-                       if (*s == ',')
-                               s++;
-                       if (*s)
-                               parity = *s++;
-                       if (*s == ',')
-                               s++;
-                       if (*s)
-                               bits = *s++;
-                       if (*s == ',')
-                               s++;
-                       if (*s == 'h')
-                               flow = 'r';
-               }
-               if (baud == 0)
-                       baud = 38400;
-               if (parity != 'n' && parity != 'o' && parity != 'e')
-                       parity = 'n';
-               if (bits != '7' && bits != '8')
-                       bits = '8';
-               if (flow == '\0')
-                       flow = 'r';
-               sprintf(console_string, " console=ttyS0,%d%c%c%c", baud,
-                       parity, bits, flow);
-               strcat(fw_getcmdline(), console_string);
-       }
-}
-#endif
-
-static void __init mips_nmi_setup(void)
-{
-       void *base;
-
-       base = cpu_has_veic ?
-               (void *)(CAC_BASE + 0xa80) :
-               (void *)(CAC_BASE + 0x380);
-#ifdef CONFIG_CPU_MICROMIPS
-       /*
-        * Decrement the exception vector address by one for microMIPS.
-        */
-       memcpy(base, (&except_vec_nmi - 1), 0x80);
-
-       /*
-        * This is a hack. We do not know if the boot loader was built with
-        * microMIPS instructions or not. If it was not, the NMI exception
-        * code at 0x80000a80 will be taken in MIPS32 mode. The hand coded
-        * assembly below forces us into microMIPS mode if we are a pure
-        * microMIPS kernel. The assembly instructions are:
-        *
-        *  3C1A8000   lui       k0,0x8000
-        *  375A0381   ori       k0,k0,0x381
-        *  03400008   jr        k0
-        *  00000000   nop
-        *
-        * The mode switch occurs by jumping to the unaligned exception
-        * vector address at 0x80000381 which would have been 0x80000380
-        * in MIPS32 mode. The jump to the unaligned address transitions
-        * us into microMIPS mode.
-        */
-       if (!cpu_has_veic) {
-               void *base2 = (void *)(CAC_BASE + 0xa80);
-               *((unsigned int *)base2) = 0x3c1a8000;
-               *((unsigned int *)base2 + 1) = 0x375a0381;
-               *((unsigned int *)base2 + 2) = 0x03400008;
-               *((unsigned int *)base2 + 3) = 0x00000000;
-               flush_icache_range((unsigned long)base2,
-                       (unsigned long)base2 + 0x10);
-       }
-#else
-       memcpy(base, &except_vec_nmi, 0x80);
-#endif
-       flush_icache_range((unsigned long)base, (unsigned long)base + 0x80);
-}
-
-static void __init mips_ejtag_setup(void)
-{
-       void *base;
-
-       base = cpu_has_veic ?
-               (void *)(CAC_BASE + 0xa00) :
-               (void *)(CAC_BASE + 0x300);
-#ifdef CONFIG_CPU_MICROMIPS
-       /* Deja vu... */
-       memcpy(base, (&except_vec_ejtag_debug - 1), 0x80);
-       if (!cpu_has_veic) {
-               void *base2 = (void *)(CAC_BASE + 0xa00);
-               *((unsigned int *)base2) = 0x3c1a8000;
-               *((unsigned int *)base2 + 1) = 0x375a0301;
-               *((unsigned int *)base2 + 2) = 0x03400008;
-               *((unsigned int *)base2 + 3) = 0x00000000;
-               flush_icache_range((unsigned long)base2,
-                       (unsigned long)base2 + 0x10);
-       }
-#else
-       memcpy(base, &except_vec_ejtag_debug, 0x80);
-#endif
-       flush_icache_range((unsigned long)base, (unsigned long)base + 0x80);
-}
-
-void __init prom_init(void)
-{
-       board_nmi_handler_setup = mips_nmi_setup;
-       board_ejtag_handler_setup = mips_ejtag_setup;
-
-       fw_init_cmdline();
-#ifdef CONFIG_EARLY_PRINTK
-       if ((strstr(fw_getcmdline(), "console=ttyS0")) != NULL)
-               fw_init_early_console(0);
-       else if ((strstr(fw_getcmdline(), "console=ttyS1")) != NULL)
-               fw_init_early_console(1);
-#endif
-#ifdef CONFIG_SERIAL_8250_CONSOLE
-       if ((strstr(fw_getcmdline(), "console=")) == NULL)
-               strcat(fw_getcmdline(), " console=ttyS0,38400n8r");
-       console_config();
-#endif
-}
-
-void __init prom_free_prom_memory(void)
-{
-}
diff --git a/arch/mips/mti-sead3/sead3-int.c b/arch/mips/mti-sead3/sead3-int.c
deleted file mode 100644 (file)
index e31e17f..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
- */
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/irqchip/mips-gic.h>
-#include <linux/io.h>
-
-#include <asm/irq_cpu.h>
-#include <asm/setup.h>
-
-#include <asm/mips-boards/sead3int.h>
-
-#define SEAD_CONFIG_GIC_PRESENT_SHF    1
-#define SEAD_CONFIG_GIC_PRESENT_MSK    (1 << SEAD_CONFIG_GIC_PRESENT_SHF)
-#define SEAD_CONFIG_BASE               0x1b100110
-#define SEAD_CONFIG_SIZE               4
-
-static void __iomem *sead3_config_reg;
-
-void __init arch_init_irq(void)
-{
-       if (!cpu_has_veic)
-               mips_cpu_irq_init();
-
-       sead3_config_reg = ioremap_nocache(SEAD_CONFIG_BASE, SEAD_CONFIG_SIZE);
-       gic_present = (__raw_readl(sead3_config_reg) &
-                      SEAD_CONFIG_GIC_PRESENT_MSK) >>
-               SEAD_CONFIG_GIC_PRESENT_SHF;
-       pr_info("GIC: %spresent\n", (gic_present) ? "" : "not ");
-       pr_info("EIC: %s\n",
-               (current_cpu_data.options & MIPS_CPU_VEIC) ?  "on" : "off");
-
-       if (gic_present)
-               gic_init(GIC_BASE_ADDR, GIC_ADDRSPACE_SZ, CPU_INT_GIC,
-                        MIPS_GIC_IRQ_BASE);
-}
-
diff --git a/arch/mips/mti-sead3/sead3-lcd.c b/arch/mips/mti-sead3/sead3-lcd.c
deleted file mode 100644 (file)
index 10b10ed..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
- */
-#include <linux/init.h>
-#include <linux/platform_device.h>
-
-static struct resource __initdata sead3_lcd_resource = {
-               .start  = 0x1f000400,
-               .end    = 0x1f00041f,
-               .flags  = IORESOURCE_MEM,
-};
-
-static __init int sead3_lcd_add(void)
-{
-       struct platform_device *pdev;
-       int retval;
-
-       /* SEAD-3 and Cobalt platforms use same display type. */
-       pdev = platform_device_alloc("cobalt-lcd", -1);
-       if (!pdev)
-               return -ENOMEM;
-
-       retval = platform_device_add_resources(pdev, &sead3_lcd_resource, 1);
-       if (retval)
-               goto err_free_device;
-
-       retval = platform_device_add(pdev);
-       if (retval)
-               goto err_free_device;
-
-       return 0;
-
-err_free_device:
-       platform_device_put(pdev);
-
-       return retval;
-}
-
-device_initcall(sead3_lcd_add);
diff --git a/arch/mips/mti-sead3/sead3-platform.c b/arch/mips/mti-sead3/sead3-platform.c
deleted file mode 100644 (file)
index 73b73ef..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
- */
-#include <linux/dma-mapping.h>
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/irqchip/mips-gic.h>
-#include <linux/leds.h>
-#include <linux/mtd/physmap.h>
-#include <linux/platform_device.h>
-#include <linux/serial_8250.h>
-#include <linux/smsc911x.h>
-
-#include <asm/mips-boards/sead3int.h>
-
-#define UART(base)                                                     \
-{                                                                      \
-       .mapbase        = base,                                         \
-       .irq            = -1,                                           \
-       .uartclk        = 14745600,                                     \
-       .iotype         = UPIO_MEM32,                                   \
-       .flags          = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, \
-       .regshift       = 2,                                            \
-}
-
-static struct plat_serial8250_port uart8250_data[] = {
-       UART(0x1f000900),   /* ttyS0 = USB   */
-       UART(0x1f000800),   /* ttyS1 = RS232 */
-       { },
-};
-
-static struct platform_device uart8250_device = {
-       .name                   = "serial8250",
-       .id                     = PLAT8250_DEV_PLATFORM2,
-       .dev                    = {
-               .platform_data  = uart8250_data,
-       },
-};
-
-static struct smsc911x_platform_config sead3_smsc911x_data = {
-       .irq_polarity   = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
-       .irq_type       = SMSC911X_IRQ_TYPE_PUSH_PULL,
-       .flags          = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS,
-       .phy_interface  = PHY_INTERFACE_MODE_MII,
-};
-
-static struct resource sead3_net_resources[] = {
-       {
-               .start                  = 0x1f010000,
-               .end                    = 0x1f01ffff,
-               .flags                  = IORESOURCE_MEM
-       }, {
-               .flags                  = IORESOURCE_IRQ
-       }
-};
-
-static struct platform_device sead3_net_device = {
-       .name                   = "smsc911x",
-       .id                     = 0,
-       .dev                    = {
-               .platform_data  = &sead3_smsc911x_data,
-       },
-       .num_resources          = ARRAY_SIZE(sead3_net_resources),
-       .resource               = sead3_net_resources
-};
-
-static struct mtd_partition sead3_mtd_partitions[] = {
-       {
-               .name =         "User FS",
-               .offset =       0x00000000,
-               .size =         0x01fc0000,
-       }, {
-               .name =         "Board Config",
-               .offset =       0x01fc0000,
-               .size =         0x00040000,
-               .mask_flags =   MTD_WRITEABLE
-       },
-};
-
-static struct physmap_flash_data sead3_flash_data = {
-       .width          = 4,
-       .nr_parts       = ARRAY_SIZE(sead3_mtd_partitions),
-       .parts          = sead3_mtd_partitions
-};
-
-static struct resource sead3_flash_resource = {
-       .start          = 0x1c000000,
-       .end            = 0x1dffffff,
-       .flags          = IORESOURCE_MEM
-};
-
-static struct platform_device sead3_flash = {
-       .name           = "physmap-flash",
-       .id             = 0,
-       .dev            = {
-               .platform_data  = &sead3_flash_data,
-       },
-       .num_resources  = 1,
-       .resource       = &sead3_flash_resource,
-};
-
-#define LEDFLAGS(bits, shift)          \
-       ((bits << 8) | (shift << 8))
-
-#define LEDBITS(id, shift, bits)       \
-       .name = id #shift,              \
-       .flags = LEDFLAGS(bits, shift)
-
-static struct led_info led_data_info[] = {
-       { LEDBITS("bit", 0, 1) },
-       { LEDBITS("bit", 1, 1) },
-       { LEDBITS("bit", 2, 1) },
-       { LEDBITS("bit", 3, 1) },
-       { LEDBITS("bit", 4, 1) },
-       { LEDBITS("bit", 5, 1) },
-       { LEDBITS("bit", 6, 1) },
-       { LEDBITS("bit", 7, 1) },
-       { LEDBITS("all", 0, 8) },
-};
-
-static struct led_platform_data led_data = {
-       .num_leds       = ARRAY_SIZE(led_data_info),
-       .leds           = led_data_info
-};
-
-static struct resource pled_resources[] = {
-       {
-               .start  = 0x1f000210,
-               .end    = 0x1f000217,
-               .flags  = IORESOURCE_MEM
-       }
-};
-
-static struct platform_device pled_device = {
-       .name                   = "sead3::pled",
-       .id                     = 0,
-       .dev                    = {
-               .platform_data  = &led_data,
-       },
-       .num_resources          = ARRAY_SIZE(pled_resources),
-       .resource               = pled_resources
-};
-
-
-static struct resource fled_resources[] = {
-       {
-               .start                  = 0x1f000218,
-               .end                    = 0x1f00021f,
-               .flags                  = IORESOURCE_MEM
-       }
-};
-
-static struct platform_device fled_device = {
-       .name                   = "sead3::fled",
-       .id                     = 0,
-       .dev                    = {
-               .platform_data  = &led_data,
-       },
-       .num_resources          = ARRAY_SIZE(fled_resources),
-       .resource               = fled_resources
-};
-
-static struct platform_device sead3_led_device = {
-        .name   = "sead3-led",
-        .id     = -1,
-};
-
-static struct resource ehci_resources[] = {
-       {
-               .start                  = 0x1b200000,
-               .end                    = 0x1b200fff,
-               .flags                  = IORESOURCE_MEM
-       }, {
-               .flags                  = IORESOURCE_IRQ
-       }
-};
-
-static u64 sead3_usbdev_dma_mask = DMA_BIT_MASK(32);
-
-static struct platform_device ehci_device = {
-       .name           = "sead3-ehci",
-       .id             = 0,
-       .dev            = {
-               .dma_mask               = &sead3_usbdev_dma_mask,
-               .coherent_dma_mask      = DMA_BIT_MASK(32)
-       },
-       .num_resources  = ARRAY_SIZE(ehci_resources),
-       .resource       = ehci_resources
-};
-
-static struct platform_device *sead3_platform_devices[] __initdata = {
-       &uart8250_device,
-       &sead3_flash,
-       &pled_device,
-       &fled_device,
-       &sead3_led_device,
-       &ehci_device,
-       &sead3_net_device,
-};
-
-static int __init sead3_platforms_device_init(void)
-{
-       if (gic_present) {
-               uart8250_data[0].irq = MIPS_GIC_IRQ_BASE + GIC_INT_UART0;
-               uart8250_data[1].irq = MIPS_GIC_IRQ_BASE + GIC_INT_UART1;
-               ehci_resources[1].start = MIPS_GIC_IRQ_BASE + GIC_INT_EHCI;
-               sead3_net_resources[1].start = MIPS_GIC_IRQ_BASE + GIC_INT_NET;
-       } else {
-               uart8250_data[0].irq = MIPS_CPU_IRQ_BASE + CPU_INT_UART0;
-               uart8250_data[1].irq = MIPS_CPU_IRQ_BASE + CPU_INT_UART1;
-               ehci_resources[1].start = MIPS_CPU_IRQ_BASE + CPU_INT_EHCI;
-               sead3_net_resources[1].start = MIPS_CPU_IRQ_BASE + CPU_INT_NET;
-       }
-
-       return platform_add_devices(sead3_platform_devices,
-                                   ARRAY_SIZE(sead3_platform_devices));
-}
-
-device_initcall(sead3_platforms_device_init);
diff --git a/arch/mips/mti-sead3/sead3-reset.c b/arch/mips/mti-sead3/sead3-reset.c
deleted file mode 100644 (file)
index e6fb244..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
- */
-#include <linux/io.h>
-#include <linux/pm.h>
-
-#include <asm/reboot.h>
-
-#define SOFTRES_REG    0x1f000050
-#define GORESET                0x4d
-
-static void mips_machine_restart(char *command)
-{
-       unsigned int __iomem *softres_reg =
-               ioremap(SOFTRES_REG, sizeof(unsigned int));
-
-       __raw_writel(GORESET, softres_reg);
-}
-
-static void mips_machine_halt(void)
-{
-       unsigned int __iomem *softres_reg =
-               ioremap(SOFTRES_REG, sizeof(unsigned int));
-
-       __raw_writel(GORESET, softres_reg);
-}
-
-static int __init mips_reboot_setup(void)
-{
-       _machine_restart = mips_machine_restart;
-       _machine_halt = mips_machine_halt;
-       pm_power_off = mips_machine_halt;
-
-       return 0;
-}
-arch_initcall(mips_reboot_setup);
diff --git a/arch/mips/mti-sead3/sead3-setup.c b/arch/mips/mti-sead3/sead3-setup.c
deleted file mode 100644 (file)
index edfcaf0..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
- * Copyright (C) 2013 Imagination Technologies Ltd.
- */
-#include <linux/init.h>
-#include <linux/libfdt.h>
-#include <linux/of_fdt.h>
-
-#include <asm/prom.h>
-#include <asm/fw/fw.h>
-
-#include <asm/mips-boards/generic.h>
-
-const char *get_system_type(void)
-{
-       return "MIPS SEAD3";
-}
-
-static uint32_t get_memsize_from_cmdline(void)
-{
-       int memsize = 0;
-       char *p = arcs_cmdline;
-       char *s = "memsize=";
-
-       p = strstr(p, s);
-       if (p) {
-               p += strlen(s);
-               memsize = memparse(p, NULL);
-       }
-
-       return memsize;
-}
-
-static uint32_t get_memsize_from_env(void)
-{
-       int memsize = 0;
-       char *p;
-
-       p = fw_getenv("memsize");
-       if (p)
-               memsize = memparse(p, NULL);
-
-       return memsize;
-}
-
-static uint32_t get_memsize(void)
-{
-       uint32_t memsize;
-
-       memsize = get_memsize_from_cmdline();
-       if (memsize)
-               return memsize;
-
-       return get_memsize_from_env();
-}
-
-static void __init parse_memsize_param(void)
-{
-       int offset;
-       const uint64_t *prop_value;
-       int prop_len;
-       uint32_t memsize = get_memsize();
-
-       if (!memsize)
-               return;
-
-       offset = fdt_path_offset(__dtb_start, "/memory");
-       if (offset > 0) {
-               uint64_t new_value;
-               /*
-                * reg contains 2 32-bits BE values, offset and size. We just
-                * want to replace the size value without affecting the offset
-                */
-               prop_value = fdt_getprop(__dtb_start, offset, "reg", &prop_len);
-               new_value = be64_to_cpu(*prop_value);
-               new_value =  (new_value & ~0xffffffffllu) | memsize;
-               fdt_setprop_inplace_u64(__dtb_start, offset, "reg", new_value);
-       }
-}
-
-void __init *plat_get_fdt(void)
-{
-       return (void *)__dtb_start;
-}
-
-void __init plat_mem_setup(void)
-{
-       /* allow command line/bootloader env to override memory size in DT */
-       parse_memsize_param();
-
-       /*
-        * Load the builtin devicetree. This causes the chosen node to be
-        * parsed resulting in our memory appearing
-        */
-       __dt_setup_arch(__dtb_start);
-}
-
-void __init device_tree_init(void)
-{
-       if (!initial_boot_params)
-               return;
-
-       unflatten_and_copy_device_tree();
-}
diff --git a/arch/mips/mti-sead3/sead3-time.c b/arch/mips/mti-sead3/sead3-time.c
deleted file mode 100644 (file)
index a120b7a..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
- */
-#include <linux/init.h>
-#include <linux/irqchip/mips-gic.h>
-
-#include <asm/cpu.h>
-#include <asm/setup.h>
-#include <asm/time.h>
-#include <asm/irq.h>
-#include <asm/mips-boards/generic.h>
-
-static void __iomem *status_reg = (void __iomem *)0xbf000410;
-
-/*
- * Estimate CPU frequency.  Sets mips_hpt_frequency as a side-effect.
- */
-static unsigned int __init estimate_cpu_frequency(void)
-{
-       unsigned int prid = read_c0_prid() & (PRID_COMP_MASK | PRID_IMP_MASK);
-       unsigned int tick = 0;
-       unsigned int freq;
-       unsigned int orig;
-       unsigned long flags;
-
-       local_irq_save(flags);
-
-       orig = readl(status_reg) & 0x2;               /* get original sample */
-       /* wait for transition */
-       while ((readl(status_reg) & 0x2) == orig)
-               ;
-       orig = orig ^ 0x2;                            /* flip the bit */
-
-       write_c0_count(0);
-
-       /* wait 1 second (the sampling clock transitions every 10ms) */
-       while (tick < 100) {
-               /* wait for transition */
-               while ((readl(status_reg) & 0x2) == orig)
-                       ;
-               orig = orig ^ 0x2;                            /* flip the bit */
-               tick++;
-       }
-
-       freq = read_c0_count();
-
-       local_irq_restore(flags);
-
-       mips_hpt_frequency = freq;
-
-       /* Adjust for processor */
-       if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) &&
-               (prid != (PRID_COMP_MIPS | PRID_IMP_25KF)))
-               freq *= 2;
-
-       freq += 5000;        /* rounding */
-       freq -= freq%10000;
-
-       return freq ;
-}
-
-void read_persistent_clock(struct timespec *ts)
-{
-       ts->tv_sec = 0;
-       ts->tv_nsec = 0;
-}
-
-int get_c0_perfcount_int(void)
-{
-       if (gic_present)
-               return gic_get_c0_perfcount_int();
-       if (cp0_perfcount_irq >= 0)
-               return MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
-       return -1;
-}
-EXPORT_SYMBOL_GPL(get_c0_perfcount_int);
-
-unsigned int get_c0_compare_int(void)
-{
-       if (gic_present)
-               return gic_get_c0_compare_int();
-       return MIPS_CPU_IRQ_BASE + cp0_compare_irq;
-}
-
-void __init plat_time_init(void)
-{
-       unsigned int est_freq;
-
-       est_freq = estimate_cpu_frequency();
-
-       pr_debug("CPU frequency %d.%02d MHz\n", (est_freq / 1000000),
-               (est_freq % 1000000) * 100 / 1000000);
-
-       mips_scroll_message();
-}
index 139ad1d7ab5e3ce9dfa8aba6b507fe04e8b8d8b9..4b821481dd4432b11103ac30fb24fa2fece707ec 100644 (file)
@@ -3,6 +3,8 @@
 #
 
 obj-y                          += pci.o
+obj-$(CONFIG_PCI_DRIVERS_LEGACY)+= pci-legacy.o
+obj-$(CONFIG_PCI_DRIVERS_GENERIC)+= pci-generic.o
 
 #
 # PCI bus host bridge specific code
index c8994c156e2ddad2c0bde912c553f66015c8d757..e99ca7702d8ad81660ab011ae4c990c0fb954f96 100644 (file)
@@ -429,7 +429,8 @@ static int alchemy_pci_probe(struct platform_device *pdev)
 
        /* Au1500 revisions older than AD have borked coherent PCI */
        if ((alchemy_get_cputype() == ALCHEMY_CPU_AU1500) &&
-           (read_c0_prid() < 0x01030202) && !coherentio) {
+           (read_c0_prid() < 0x01030202) &&
+           (coherentio == IO_COHERENCE_DISABLED)) {
                val = __raw_readl(ctx->regs + PCI_REG_CONFIG);
                val |= PCI_CONFIG_NC;
                __raw_writel(val, ctx->regs + PCI_REG_CONFIG);
index 7db963deec737747de9f2e7dedcdf049a93a6115..bdf87b43633fe51694218eccc7e3e767eb3ba99d 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/pci.h>
 #include <linux/pci_regs.h>
 #include <linux/interrupt.h>
-#include <linux/module.h>
+#include <linux/init.h>
 #include <linux/platform_device.h>
 
 #include <asm/mach-ath79/ar71xx_regs.h>
index 2013dad700dfa9c38c7618067c8fb78fbd0f7350..1e23c8d587bdef78db97ee553439d1f1eb3fe3ba 100644 (file)
@@ -11,7 +11,7 @@
 
 #include <linux/irq.h>
 #include <linux/pci.h>
-#include <linux/module.h>
+#include <linux/init.h>
 #include <linux/platform_device.h>
 #include <asm/mach-ath79/ath79.h>
 #include <asm/mach-ath79/ar71xx_regs.h>
diff --git a/arch/mips/pci/pci-generic.c b/arch/mips/pci/pci-generic.c
new file mode 100644 (file)
index 0000000..dce304d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.com>
+ *
+ * pcibios_align_resource taken from arch/arm/kernel/bios32.c.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/pci.h>
+
+/*
+ * We need to avoid collisions with `mirrored' VGA ports
+ * and other strange ISA hardware, so we always want the
+ * addresses to be allocated in the 0x000-0x0ff region
+ * modulo 0x400.
+ *
+ * Why? Because some silly external IO cards only decode
+ * the low 10 bits of the IO address. The 0x00-0xff region
+ * is reserved for motherboard devices that decode all 16
+ * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
+ * but we want to try to avoid allocating at 0x2900-0x2bff
+ * which might have be mirrored at 0x0100-0x03ff..
+ */
+resource_size_t pcibios_align_resource(void *data, const struct resource *res,
+                               resource_size_t size, resource_size_t align)
+{
+       struct pci_dev *dev = data;
+       resource_size_t start = res->start;
+       struct pci_host_bridge *host_bridge;
+
+       if (res->flags & IORESOURCE_IO && start & 0x300)
+               start = (start + 0x3ff) & ~0x3ff;
+
+       start = (start + align - 1) & ~(align - 1);
+
+       host_bridge = pci_find_host_bridge(dev->bus);
+
+       if (host_bridge->align_resource)
+               return host_bridge->align_resource(dev, res,
+                               start, size, align);
+
+       return start;
+}
+
+void pcibios_fixup_bus(struct pci_bus *bus)
+{
+       pci_read_bridge_bases(bus);
+}
index b9deab17ccf246130760565cc1b387fdea5997ca..f18f887f481d8ada98e62b00dd600069929db253 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/delay.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
-#include <linux/module.h>
 #include <linux/clk.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
@@ -234,7 +233,6 @@ static const struct of_device_id ltq_pci_match[] = {
        { .compatible = "lantiq,pci-xway" },
        {},
 };
-MODULE_DEVICE_TABLE(of, ltq_pci_match);
 
 static struct platform_driver ltq_pci_driver = {
        .probe = ltq_pci_probe,
diff --git a/arch/mips/pci/pci-legacy.c b/arch/mips/pci/pci-legacy.c
new file mode 100644 (file)
index 0000000..014649b
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2003, 04, 11 Ralf Baechle (ralf@linux-mips.org)
+ * Copyright (C) 2011 Wind River Systems,
+ *   written by Ralf Baechle (ralf@linux-mips.org)
+ */
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/bootmem.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/of_address.h>
+
+#include <asm/cpu-info.h>
+
+/*
+ * If PCI_PROBE_ONLY in pci_flags is set, we don't change any PCI resource
+ * assignments.
+ */
+
+/*
+ * The PCI controller list.
+ */
+static LIST_HEAD(controllers);
+
+static int pci_initialized;
+
+/*
+ * We need to avoid collisions with `mirrored' VGA ports
+ * and other strange ISA hardware, so we always want the
+ * addresses to be allocated in the 0x000-0x0ff region
+ * modulo 0x400.
+ *
+ * Why? Because some silly external IO cards only decode
+ * the low 10 bits of the IO address. The 0x00-0xff region
+ * is reserved for motherboard devices that decode all 16
+ * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
+ * but we want to try to avoid allocating at 0x2900-0x2bff
+ * which might have be mirrored at 0x0100-0x03ff..
+ */
+resource_size_t
+pcibios_align_resource(void *data, const struct resource *res,
+                      resource_size_t size, resource_size_t align)
+{
+       struct pci_dev *dev = data;
+       struct pci_controller *hose = dev->sysdata;
+       resource_size_t start = res->start;
+
+       if (res->flags & IORESOURCE_IO) {
+               /* Make sure we start at our min on all hoses */
+               if (start < PCIBIOS_MIN_IO + hose->io_resource->start)
+                       start = PCIBIOS_MIN_IO + hose->io_resource->start;
+
+               /*
+                * Put everything into 0x00-0xff region modulo 0x400
+                */
+               if (start & 0x300)
+                       start = (start + 0x3ff) & ~0x3ff;
+       } else if (res->flags & IORESOURCE_MEM) {
+               /* Make sure we start at our min on all hoses */
+               if (start < PCIBIOS_MIN_MEM + hose->mem_resource->start)
+                       start = PCIBIOS_MIN_MEM + hose->mem_resource->start;
+       }
+
+       return start;
+}
+
+static void pcibios_scanbus(struct pci_controller *hose)
+{
+       static int next_busno;
+       static int need_domain_info;
+       LIST_HEAD(resources);
+       struct pci_bus *bus;
+
+       if (hose->get_busno && pci_has_flag(PCI_PROBE_ONLY))
+               next_busno = (*hose->get_busno)();
+
+       pci_add_resource_offset(&resources,
+                               hose->mem_resource, hose->mem_offset);
+       pci_add_resource_offset(&resources,
+                               hose->io_resource, hose->io_offset);
+       pci_add_resource_offset(&resources,
+                               hose->busn_resource, hose->busn_offset);
+       bus = pci_scan_root_bus(NULL, next_busno, hose->pci_ops, hose,
+                               &resources);
+       hose->bus = bus;
+
+       need_domain_info = need_domain_info || pci_domain_nr(bus);
+       set_pci_need_domain_info(hose, need_domain_info);
+
+       if (!bus) {
+               pci_free_resource_list(&resources);
+               return;
+       }
+
+       next_busno = bus->busn_res.end + 1;
+       /* Don't allow 8-bit bus number overflow inside the hose -
+          reserve some space for bridges. */
+       if (next_busno > 224) {
+               next_busno = 0;
+               need_domain_info = 1;
+       }
+
+       /*
+        * We insert PCI resources into the iomem_resource and
+        * ioport_resource trees in either pci_bus_claim_resources()
+        * or pci_bus_assign_resources().
+        */
+       if (pci_has_flag(PCI_PROBE_ONLY)) {
+               pci_bus_claim_resources(bus);
+       } else {
+               pci_bus_size_bridges(bus);
+               pci_bus_assign_resources(bus);
+       }
+       pci_bus_add_devices(bus);
+}
+
+#ifdef CONFIG_OF
+void pci_load_of_ranges(struct pci_controller *hose, struct device_node *node)
+{
+       struct of_pci_range range;
+       struct of_pci_range_parser parser;
+
+       pr_info("PCI host bridge %s ranges:\n", node->full_name);
+       hose->of_node = node;
+
+       if (of_pci_range_parser_init(&parser, node))
+               return;
+
+       for_each_of_pci_range(&parser, &range) {
+               struct resource *res = NULL;
+
+               switch (range.flags & IORESOURCE_TYPE_BITS) {
+               case IORESOURCE_IO:
+                       pr_info("  IO 0x%016llx..0x%016llx\n",
+                               range.cpu_addr,
+                               range.cpu_addr + range.size - 1);
+                       hose->io_map_base =
+                               (unsigned long)ioremap(range.cpu_addr,
+                                                      range.size);
+                       res = hose->io_resource;
+                       break;
+               case IORESOURCE_MEM:
+                       pr_info(" MEM 0x%016llx..0x%016llx\n",
+                               range.cpu_addr,
+                               range.cpu_addr + range.size - 1);
+                       res = hose->mem_resource;
+                       break;
+               }
+               if (res != NULL)
+                       of_pci_range_to_resource(&range, node, res);
+       }
+}
+
+struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
+{
+       struct pci_controller *hose = bus->sysdata;
+
+       return of_node_get(hose->of_node);
+}
+#endif
+
+static DEFINE_MUTEX(pci_scan_mutex);
+
+void register_pci_controller(struct pci_controller *hose)
+{
+       struct resource *parent;
+
+       parent = hose->mem_resource->parent;
+       if (!parent)
+               parent = &iomem_resource;
+
+       if (request_resource(parent, hose->mem_resource) < 0)
+               goto out;
+
+       parent = hose->io_resource->parent;
+       if (!parent)
+               parent = &ioport_resource;
+
+       if (request_resource(parent, hose->io_resource) < 0) {
+               release_resource(hose->mem_resource);
+               goto out;
+       }
+
+       INIT_LIST_HEAD(&hose->list);
+       list_add(&hose->list, &controllers);
+
+       /*
+        * Do not panic here but later - this might happen before console init.
+        */
+       if (!hose->io_map_base) {
+               printk(KERN_WARNING
+                      "registering PCI controller with io_map_base unset\n");
+       }
+
+       /*
+        * Scan the bus if it is register after the PCI subsystem
+        * initialization.
+        */
+       if (pci_initialized) {
+               mutex_lock(&pci_scan_mutex);
+               pcibios_scanbus(hose);
+               mutex_unlock(&pci_scan_mutex);
+       }
+
+       return;
+
+out:
+       printk(KERN_WARNING
+              "Skipping PCI bus scan due to resource conflict\n");
+}
+
+static int __init pcibios_init(void)
+{
+       struct pci_controller *hose;
+
+       /* Scan all of the recorded PCI controllers.  */
+       list_for_each_entry(hose, &controllers, list)
+               pcibios_scanbus(hose);
+
+       pci_fixup_irqs(pci_common_swizzle, pcibios_map_irq);
+
+       pci_initialized = 1;
+
+       return 0;
+}
+
+subsys_initcall(pcibios_init);
+
+static int pcibios_enable_resources(struct pci_dev *dev, int mask)
+{
+       u16 cmd, old_cmd;
+       int idx;
+       struct resource *r;
+
+       pci_read_config_word(dev, PCI_COMMAND, &cmd);
+       old_cmd = cmd;
+       for (idx=0; idx < PCI_NUM_RESOURCES; idx++) {
+               /* Only set up the requested stuff */
+               if (!(mask & (1<<idx)))
+                       continue;
+
+               r = &dev->resource[idx];
+               if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
+                       continue;
+               if ((idx == PCI_ROM_RESOURCE) &&
+                               (!(r->flags & IORESOURCE_ROM_ENABLE)))
+                       continue;
+               if (!r->start && r->end) {
+                       printk(KERN_ERR "PCI: Device %s not available "
+                              "because of resource collisions\n",
+                              pci_name(dev));
+                       return -EINVAL;
+               }
+               if (r->flags & IORESOURCE_IO)
+                       cmd |= PCI_COMMAND_IO;
+               if (r->flags & IORESOURCE_MEM)
+                       cmd |= PCI_COMMAND_MEMORY;
+       }
+       if (cmd != old_cmd) {
+               printk("PCI: Enabling device %s (%04x -> %04x)\n",
+                      pci_name(dev), old_cmd, cmd);
+               pci_write_config_word(dev, PCI_COMMAND, cmd);
+       }
+       return 0;
+}
+
+int pcibios_enable_device(struct pci_dev *dev, int mask)
+{
+       int err;
+
+       if ((err = pcibios_enable_resources(dev, mask)) < 0)
+               return err;
+
+       return pcibios_plat_dev_init(dev);
+}
+
+void pcibios_fixup_bus(struct pci_bus *bus)
+{
+       struct pci_dev *dev = bus->self;
+
+       if (pci_has_flag(PCI_PROBE_ONLY) && dev &&
+           (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+               pci_read_bridge_bases(bus);
+       }
+}
+
+char * (*pcibios_plat_setup)(char *str) __initdata;
+
+char *__init pcibios_setup(char *str)
+{
+       if (pcibios_plat_setup)
+               return pcibios_plat_setup(str);
+       return str;
+}
index 6ce81620169956564ab96a0cda2b22e3cf756e27..628c5132b3d8b254ad0c590ef9a606af05412972 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
-#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_pci.h>
@@ -407,13 +406,11 @@ static const struct of_device_id mt7620_pci_ids[] = {
        { .compatible = "mediatek,mt7620-pci" },
        {},
 };
-MODULE_DEVICE_TABLE(of, mt7620_pci_ids);
 
 static struct platform_driver mt7620_pci_driver = {
        .probe = mt7620_pci_probe,
        .driver = {
                .name = "mt7620-pci",
-               .owner = THIS_MODULE,
                .of_match_table = of_match_ptr(mt7620_pci_ids),
        },
 };
index c258cd406fbbe39b2e657b60e5e43545526d9a4e..308d051fc45cd5d25e96c4157abbfeabd32cc5c0 100644 (file)
@@ -204,6 +204,8 @@ const char *octeon_get_pci_interrupts(void)
         * Interrupt Number (INTA# = 0, INTB# = 1, INTC# = 2, and
         * INTD# = 3)
         */
+       if (of_machine_is_compatible("dlink,dsr-500n"))
+               return "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
        switch (octeon_bootinfo->board_type) {
        case CVMX_BOARD_TYPE_NAO38:
                /* This is really the NAC38 */
index f2a1050168d9592c0a0940b9f996f4c1141285cc..d6360fe73d058c5733274fb1b163c393a3f0e14c 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/pci.h>
 #include <linux/io.h>
 #include <linux/init.h>
-#include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/of_irq.h>
 #include <linux/of_pci.h>
@@ -260,7 +259,6 @@ static const struct of_device_id rt288x_pci_match[] = {
        { .compatible = "ralink,rt288x-pci" },
        {},
 };
-MODULE_DEVICE_TABLE(of, rt288x_pci_match);
 
 static struct platform_driver rt288x_pci_driver = {
        .probe = rt288x_pci_probe,
index 53a42b07008b99f2043ac1da444b9732c4af8c2b..3520e9b414e7b91dfedc59abeb37d757579e475a 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
-#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_pci.h>
@@ -580,7 +579,6 @@ static const struct of_device_id rt3883_pci_ids[] = {
        { .compatible = "ralink,rt3883-pci" },
        {},
 };
-MODULE_DEVICE_TABLE(of, rt3883_pci_ids);
 
 static struct platform_driver rt3883_pci_driver = {
        .probe = rt3883_pci_probe,
index b4c02f29663e180aeb5635aba2f4d9eabd24980c..f6325fa657fb6538dbbc512d53254cf611b60aa1 100644 (file)
 
 #include <asm/cpu-info.h>
 
-/*
- * If PCI_PROBE_ONLY in pci_flags is set, we don't change any PCI resource
- * assignments.
- */
-
-/*
- * The PCI controller list.
- */
-
-static struct pci_controller *hose_head, **hose_tail = &hose_head;
-
 unsigned long PCIBIOS_MIN_IO;
-unsigned long PCIBIOS_MIN_MEM;
-
-static int pci_initialized;
-
-/*
- * We need to avoid collisions with `mirrored' VGA ports
- * and other strange ISA hardware, so we always want the
- * addresses to be allocated in the 0x000-0x0ff region
- * modulo 0x400.
- *
- * Why? Because some silly external IO cards only decode
- * the low 10 bits of the IO address. The 0x00-0xff region
- * is reserved for motherboard devices that decode all 16
- * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
- * but we want to try to avoid allocating at 0x2900-0x2bff
- * which might have be mirrored at 0x0100-0x03ff..
- */
-resource_size_t
-pcibios_align_resource(void *data, const struct resource *res,
-                      resource_size_t size, resource_size_t align)
-{
-       struct pci_dev *dev = data;
-       struct pci_controller *hose = dev->sysdata;
-       resource_size_t start = res->start;
-
-       if (res->flags & IORESOURCE_IO) {
-               /* Make sure we start at our min on all hoses */
-               if (start < PCIBIOS_MIN_IO + hose->io_resource->start)
-                       start = PCIBIOS_MIN_IO + hose->io_resource->start;
-
-               /*
-                * Put everything into 0x00-0xff region modulo 0x400
-                */
-               if (start & 0x300)
-                       start = (start + 0x3ff) & ~0x3ff;
-       } else if (res->flags & IORESOURCE_MEM) {
-               /* Make sure we start at our min on all hoses */
-               if (start < PCIBIOS_MIN_MEM + hose->mem_resource->start)
-                       start = PCIBIOS_MIN_MEM + hose->mem_resource->start;
-       }
-
-       return start;
-}
-
-static void pcibios_scanbus(struct pci_controller *hose)
-{
-       static int next_busno;
-       static int need_domain_info;
-       LIST_HEAD(resources);
-       struct pci_bus *bus;
-
-       if (hose->get_busno && pci_has_flag(PCI_PROBE_ONLY))
-               next_busno = (*hose->get_busno)();
-
-       pci_add_resource_offset(&resources,
-                               hose->mem_resource, hose->mem_offset);
-       pci_add_resource_offset(&resources,
-                               hose->io_resource, hose->io_offset);
-       pci_add_resource_offset(&resources,
-                               hose->busn_resource, hose->busn_offset);
-       bus = pci_scan_root_bus(NULL, next_busno, hose->pci_ops, hose,
-                               &resources);
-       hose->bus = bus;
-
-       need_domain_info = need_domain_info || hose->index;
-       hose->need_domain_info = need_domain_info;
-
-       if (!bus) {
-               pci_free_resource_list(&resources);
-               return;
-       }
-
-       next_busno = bus->busn_res.end + 1;
-       /* Don't allow 8-bit bus number overflow inside the hose -
-          reserve some space for bridges. */
-       if (next_busno > 224) {
-               next_busno = 0;
-               need_domain_info = 1;
-       }
-
-       /*
-        * We insert PCI resources into the iomem_resource and
-        * ioport_resource trees in either pci_bus_claim_resources()
-        * or pci_bus_assign_resources().
-        */
-       if (pci_has_flag(PCI_PROBE_ONLY)) {
-               pci_bus_claim_resources(bus);
-       } else {
-               pci_bus_size_bridges(bus);
-               pci_bus_assign_resources(bus);
-       }
-       pci_bus_add_devices(bus);
-}
-
-#ifdef CONFIG_OF
-void pci_load_of_ranges(struct pci_controller *hose, struct device_node *node)
-{
-       struct of_pci_range range;
-       struct of_pci_range_parser parser;
-
-       pr_info("PCI host bridge %s ranges:\n", node->full_name);
-       hose->of_node = node;
-
-       if (of_pci_range_parser_init(&parser, node))
-               return;
-
-       for_each_of_pci_range(&parser, &range) {
-               struct resource *res = NULL;
-
-               switch (range.flags & IORESOURCE_TYPE_BITS) {
-               case IORESOURCE_IO:
-                       pr_info("  IO 0x%016llx..0x%016llx\n",
-                               range.cpu_addr,
-                               range.cpu_addr + range.size - 1);
-                       hose->io_map_base =
-                               (unsigned long)ioremap(range.cpu_addr,
-                                                      range.size);
-                       res = hose->io_resource;
-                       break;
-               case IORESOURCE_MEM:
-                       pr_info(" MEM 0x%016llx..0x%016llx\n",
-                               range.cpu_addr,
-                               range.cpu_addr + range.size - 1);
-                       res = hose->mem_resource;
-                       break;
-               }
-               if (res != NULL)
-                       of_pci_range_to_resource(&range, node, res);
-       }
-}
-
-struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
-{
-       struct pci_controller *hose = bus->sysdata;
-
-       return of_node_get(hose->of_node);
-}
-#endif
-
-static DEFINE_MUTEX(pci_scan_mutex);
-
-void register_pci_controller(struct pci_controller *hose)
-{
-       struct resource *parent;
-
-       parent = hose->mem_resource->parent;
-       if (!parent)
-               parent = &iomem_resource;
-
-       if (request_resource(parent, hose->mem_resource) < 0)
-               goto out;
-
-       parent = hose->io_resource->parent;
-       if (!parent)
-               parent = &ioport_resource;
-
-       if (request_resource(parent, hose->io_resource) < 0) {
-               release_resource(hose->mem_resource);
-               goto out;
-       }
-
-       *hose_tail = hose;
-       hose_tail = &hose->next;
-
-       /*
-        * Do not panic here but later - this might happen before console init.
-        */
-       if (!hose->io_map_base) {
-               printk(KERN_WARNING
-                      "registering PCI controller with io_map_base unset\n");
-       }
-
-       /*
-        * Scan the bus if it is register after the PCI subsystem
-        * initialization.
-        */
-       if (pci_initialized) {
-               mutex_lock(&pci_scan_mutex);
-               pcibios_scanbus(hose);
-               mutex_unlock(&pci_scan_mutex);
-       }
-
-       return;
+EXPORT_SYMBOL(PCIBIOS_MIN_IO);
 
-out:
-       printk(KERN_WARNING
-              "Skipping PCI bus scan due to resource conflict\n");
-}
+unsigned long PCIBIOS_MIN_MEM;
+EXPORT_SYMBOL(PCIBIOS_MIN_MEM);
 
-static void __init pcibios_set_cache_line_size(void)
+static int __init pcibios_set_cache_line_size(void)
 {
        struct cpuinfo_mips *c = &current_cpu_data;
        unsigned int lsize;
@@ -239,92 +44,9 @@ static void __init pcibios_set_cache_line_size(void)
        pci_dfl_cache_line_size = lsize >> 2;
 
        pr_debug("PCI: pci_cache_line_size set to %d bytes\n", lsize);
-}
-
-static int __init pcibios_init(void)
-{
-       struct pci_controller *hose;
-
-       pcibios_set_cache_line_size();
-
-       /* Scan all of the recorded PCI controllers.  */
-       for (hose = hose_head; hose; hose = hose->next)
-               pcibios_scanbus(hose);
-
-       pci_fixup_irqs(pci_common_swizzle, pcibios_map_irq);
-
-       pci_initialized = 1;
-
-       return 0;
-}
-
-subsys_initcall(pcibios_init);
-
-static int pcibios_enable_resources(struct pci_dev *dev, int mask)
-{
-       u16 cmd, old_cmd;
-       int idx;
-       struct resource *r;
-
-       pci_read_config_word(dev, PCI_COMMAND, &cmd);
-       old_cmd = cmd;
-       for (idx=0; idx < PCI_NUM_RESOURCES; idx++) {
-               /* Only set up the requested stuff */
-               if (!(mask & (1<<idx)))
-                       continue;
-
-               r = &dev->resource[idx];
-               if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
-                       continue;
-               if ((idx == PCI_ROM_RESOURCE) &&
-                               (!(r->flags & IORESOURCE_ROM_ENABLE)))
-                       continue;
-               if (!r->start && r->end) {
-                       printk(KERN_ERR "PCI: Device %s not available "
-                              "because of resource collisions\n",
-                              pci_name(dev));
-                       return -EINVAL;
-               }
-               if (r->flags & IORESOURCE_IO)
-                       cmd |= PCI_COMMAND_IO;
-               if (r->flags & IORESOURCE_MEM)
-                       cmd |= PCI_COMMAND_MEMORY;
-       }
-       if (cmd != old_cmd) {
-               printk("PCI: Enabling device %s (%04x -> %04x)\n",
-                      pci_name(dev), old_cmd, cmd);
-               pci_write_config_word(dev, PCI_COMMAND, cmd);
-       }
        return 0;
 }
-
-unsigned int pcibios_assign_all_busses(void)
-{
-       return 1;
-}
-
-int pcibios_enable_device(struct pci_dev *dev, int mask)
-{
-       int err;
-
-       if ((err = pcibios_enable_resources(dev, mask)) < 0)
-               return err;
-
-       return pcibios_plat_dev_init(dev);
-}
-
-void pcibios_fixup_bus(struct pci_bus *bus)
-{
-       struct pci_dev *dev = bus->self;
-
-       if (pci_has_flag(PCI_PROBE_ONLY) && dev &&
-           (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
-               pci_read_bridge_bases(bus);
-       }
-}
-
-EXPORT_SYMBOL(PCIBIOS_MIN_IO);
-EXPORT_SYMBOL(PCIBIOS_MIN_MEM);
+arch_initcall(pcibios_set_cache_line_size);
 
 void pci_resource_to_user(const struct pci_dev *dev, int bar,
                          const struct resource *rsrc, resource_size_t *start,
@@ -359,12 +81,3 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
        return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
                vma->vm_end - vma->vm_start, vma->vm_page_prot);
 }
-
-char * (*pcibios_plat_setup)(char *str) __initdata;
-
-char *__init pcibios_setup(char *str)
-{
-       if (pcibios_plat_setup)
-               return pcibios_plat_setup(str);
-       return str;
-}
index 99f3db4f0a9b1b060476e6d5873fd45dd15a649b..9f672ceb089b9086b1fadcd0a3a487d2307beb69 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/interrupt.h>
 #include <linux/time.h>
 #include <linux/delay.h>
-#include <linux/module.h>
+#include <linux/moduleparam.h>
 
 #include <asm/octeon/octeon.h>
 #include <asm/octeon/cvmx-npei-defs.h>
index 3cd357737a267c38dc20b85724b723f37f26bd01..7cf4eb50fc7211f93219dbe23cb47581cac32f3a 100644 (file)
@@ -232,12 +232,8 @@ static struct platform_device *pnx833x_platform_devices[] __initdata = {
 
 static int __init pnx833x_platform_init(void)
 {
-       int res;
-
-       res = platform_add_devices(pnx833x_platform_devices,
-                                  ARRAY_SIZE(pnx833x_platform_devices));
-
-       return res;
+       return platform_add_devices(pnx833x_platform_devices,
+                                   ARRAY_SIZE(pnx833x_platform_devices));
 }
 
 arch_initcall(pnx833x_platform_init);
index b0343ff336c5fe6dd173af86cfb341129144a725..8077ff39bdeabb602680342f0d8e436b5f9bf4cc 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * Ralink RT2880 timer
+ * Author: John Crispin
+ *
  * 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.
@@ -6,7 +9,6 @@
  * Copyright (C) 2013 John Crispin <john@phrozen.org>
 */
 
-#include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/timer.h>
@@ -152,33 +154,17 @@ static int rt_timer_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int rt_timer_remove(struct platform_device *pdev)
-{
-       struct rt_timer *rt = platform_get_drvdata(pdev);
-
-       rt_timer_disable(rt);
-       rt_timer_free(rt);
-
-       return 0;
-}
-
 static const struct of_device_id rt_timer_match[] = {
        { .compatible = "ralink,rt2880-timer" },
        {},
 };
-MODULE_DEVICE_TABLE(of, rt_timer_match);
 
 static struct platform_driver rt_timer_driver = {
        .probe = rt_timer_probe,
-       .remove = rt_timer_remove,
        .driver = {
-               .name           = "rt-timer",
-               .of_match_table = rt_timer_match
+               .name                   = "rt-timer",
+               .of_match_table         = rt_timer_match,
+               .suppress_bind_attrs    = true,
        },
 };
-
-module_platform_driver(rt_timer_driver);
-
-MODULE_DESCRIPTION("Ralink RT2880 timer");
-MODULE_AUTHOR("John Crispin <john@phrozen.org");
-MODULE_LICENSE("GPL");
+builtin_platform_driver(rt_timer_driver);
index 8c337d60f790db9f0ae31c8a64872acfe734cb9b..42923478d45ca363b2c8eca403138dfa4aedcc61 100644 (file)
@@ -20,7 +20,7 @@ config MACH_TXX9
        select SYS_SUPPORTS_32BIT_KERNEL
        select SYS_SUPPORTS_LITTLE_ENDIAN
        select SYS_SUPPORTS_BIG_ENDIAN
-       select HAVE_CLK
+       select COMMON_CLK
 
 config TOSHIBA_JMR3927
        bool "Toshiba JMR-TX3927 board"
index 1f6bc9a3036c0976b54c4b1f85abd27bd7d6b598..285d84e5c7b92cf52785a6aa21393c67a6301c68 100644 (file)
@@ -29,12 +29,8 @@ static int __init
 early_read_config_word(struct pci_controller *hose,
                       int top_bus, int bus, int devfn, int offset, u16 *value)
 {
-       struct pci_dev fake_dev;
        struct pci_bus fake_bus;
 
-       fake_dev.bus = &fake_bus;
-       fake_dev.sysdata = hose;
-       fake_dev.devfn = devfn;
        fake_bus.number = bus;
        fake_bus.sysdata = hose;
        fake_bus.ops = hose->pci_ops;
@@ -45,7 +41,7 @@ early_read_config_word(struct pci_controller *hose,
        else
                fake_bus.parent = NULL;
 
-       return pci_read_config_word(&fake_dev, offset, value);
+       return pci_bus_read_config_word(&fake_bus, devfn, offset, value);
 }
 
 int __init txx9_pci66_check(struct pci_controller *hose, int top_bus,
index ada92db92f87d91a68a32d8747313eaf20bed021..a1d98b5c8fd6757683d1e9c23196660676006a54 100644 (file)
@@ -15,7 +15,8 @@
 #include <linux/interrupt.h>
 #include <linux/string.h>
 #include <linux/module.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
 #include <linux/err.h>
 #include <linux/gpio/driver.h>
 #include <linux/platform_device.h>
@@ -83,40 +84,6 @@ int txx9_ccfg_toeon __initdata;
 int txx9_ccfg_toeon __initdata = 1;
 #endif
 
-/* Minimum CLK support */
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
-       if (!strcmp(id, "spi-baseclk"))
-               return (struct clk *)((unsigned long)txx9_gbus_clock / 2 / 2);
-       if (!strcmp(id, "imbus_clk"))
-               return (struct clk *)((unsigned long)txx9_gbus_clock / 2);
-       return ERR_PTR(-ENOENT);
-}
-EXPORT_SYMBOL(clk_get);
-
-int clk_enable(struct clk *clk)
-{
-       return 0;
-}
-EXPORT_SYMBOL(clk_enable);
-
-void clk_disable(struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_disable);
-
-unsigned long clk_get_rate(struct clk *clk)
-{
-       return (unsigned long)clk;
-}
-EXPORT_SYMBOL(clk_get_rate);
-
-void clk_put(struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_put);
-
 #define BOARD_VEC(board)       extern struct txx9_board_vec board;
 #include <asm/txx9/boards.h>
 #undef BOARD_VEC
@@ -560,8 +527,41 @@ void __init plat_time_init(void)
        txx9_board_vec->time_init();
 }
 
+static void txx9_clk_init(void)
+{
+       struct clk_hw *hw;
+       int error;
+
+       hw = clk_hw_register_fixed_rate(NULL, "gbus", NULL, 0, txx9_gbus_clock);
+       if (IS_ERR(hw)) {
+               error = PTR_ERR(hw);
+               goto fail;
+       }
+
+       hw = clk_hw_register_fixed_factor(NULL, "imbus", "gbus", 0, 1, 2);
+       error = clk_hw_register_clkdev(hw, "imbus_clk", NULL);
+       if (error)
+               goto fail;
+
+#ifdef CONFIG_CPU_TX49XX
+       if (TX4938_REV_PCODE() == 0x4938) {
+               hw = clk_hw_register_fixed_factor(NULL, "spi", "gbus", 0, 1, 4);
+               error = clk_hw_register_clkdev(hw, "spi-baseclk", NULL);
+               if (error)
+                       goto fail;
+       }
+#endif
+
+       return;
+
+fail:
+       pr_err("Failed to register clocks: %d\n", error);
+}
+
 static int __init _txx9_arch_init(void)
 {
+       txx9_clk_init();
+
        if (txx9_board_vec->arch_init)
                txx9_board_vec->arch_init();
        return 0;
index 110e05c3eb8fb638c1b75dbeb68db81d96a26e50..d3b83a92cf26c7075939bd23ae0aeb2e083921d2 100644 (file)
@@ -92,7 +92,6 @@ void __init tx3927_setup(void)
        /* PIO */
        __raw_writel(0, &tx3927_pioptr->maskcpu);
        __raw_writel(0, &tx3927_pioptr->maskext);
-       txx9_gpio_init(TX3927_PIO_REG, 0, 16);
 
        conf = read_c0_conf();
        if (conf & TX39_CONF_DCE) {
index a4664cb6c1e183712c2f60a430e082f231b27ddc..8d8011570b1dbe990567a848d52bf513729e18c6 100644 (file)
@@ -215,7 +215,6 @@ void __init tx4927_setup(void)
                txx9_tmr_init(TX4927_TMR_REG(i) & 0xfffffffffULL);
 
        /* PIO */
-       txx9_gpio_init(TX4927_PIO_REG & 0xfffffffffULL, 0, TX4927_NUM_PIO);
        __raw_writel(0, &tx4927_pioptr->maskcpu);
        __raw_writel(0, &tx4927_pioptr->maskext);
 
index 58cdb2aba5e1ba72cac3235974919eb26ffad254..ba265bf1fd06703645cb4a353c3100013741a324 100644 (file)
@@ -241,7 +241,6 @@ void __init tx4938_setup(void)
                txx9_tmr_init(TX4938_TMR_REG(i) & 0xfffffffffULL);
 
        /* PIO */
-       txx9_gpio_init(TX4938_PIO_REG & 0xfffffffffULL, 0, TX4938_NUM_PIO);
        __raw_writel(0, &tx4938_pioptr->maskcpu);
        __raw_writel(0, &tx4938_pioptr->maskext);
 
index 3206f76f300b727a91d5566c92bbc64cddd82be0..a455166dc6d44fe7418f0b66823e49ce188e690b 100644 (file)
@@ -142,8 +142,6 @@ static void __init jmr3927_board_init(void)
 
        /* PIO[15:12] connected to LEDs */
        __raw_writel(0x0000f000, &tx3927_pioptr->dir);
-       gpio_request(11, "dipsw1");
-       gpio_request(10, "dipsw2");
 
        jmr3927_pci_setup();
 
@@ -204,6 +202,14 @@ static void __init jmr3927_device_init(void)
        txx9_iocled_init(iocled_base, -1, 8, 1, "green", NULL);
 }
 
+static void __init jmr3927_arch_init(void)
+{
+       txx9_gpio_init(TX3927_PIO_REG, 0, 16);
+
+       gpio_request(11, "dipsw1");
+       gpio_request(10, "dipsw2");
+}
+
 struct txx9_board_vec jmr3927_vec __initdata = {
        .system = "Toshiba JMR_TX3927",
        .prom_init = jmr3927_prom_init,
@@ -211,6 +217,7 @@ struct txx9_board_vec jmr3927_vec __initdata = {
        .irq_setup = jmr3927_irq_setup,
        .time_init = jmr3927_time_init,
        .device_init = jmr3927_device_init,
+       .arch_init = jmr3927_arch_init,
 #ifdef CONFIG_PCI
        .pci_map_irq = jmr3927_pci_map_irq,
 #endif
index 3c516ef625e57d2da5aee577917dd1e5d1ee318b..f5b367e20dff1cfa1f0a1b3f3a1e86f04f12dc6e 100644 (file)
@@ -52,6 +52,7 @@
 #include <linux/leds.h>
 #include <asm/io.h>
 #include <asm/reboot.h>
+#include <asm/txx9pio.h>
 #include <asm/txx9/generic.h>
 #include <asm/txx9/pci.h>
 #include <asm/txx9/rbtx4927.h>
@@ -151,20 +152,37 @@ static void __init tx4937_pci_setup(void)
        }
        tx4938_setup_pcierr_irq();
 }
+#else
+static inline void tx4927_pci_setup(void) {}
+static inline void tx4937_pci_setup(void) {}
+#endif /* CONFIG_PCI */
+
+static void __init rbtx4927_gpio_init(void)
+{
+       /* TX4927-SIO DTR on (PIO[15]) */
+       gpio_request(15, "sio-dtr");
+       gpio_direction_output(15, 1);
+
+       tx4927_sio_init(0, 0);
+}
 
 static void __init rbtx4927_arch_init(void)
 {
+       txx9_gpio_init(TX4927_PIO_REG & 0xfffffffffULL, 0, TX4927_NUM_PIO);
+
+       rbtx4927_gpio_init();
+
        tx4927_pci_setup();
 }
 
 static void __init rbtx4937_arch_init(void)
 {
+       txx9_gpio_init(TX4938_PIO_REG & 0xfffffffffULL, 0, TX4938_NUM_PIO);
+
+       rbtx4927_gpio_init();
+
        tx4937_pci_setup();
 }
-#else
-#define rbtx4927_arch_init NULL
-#define rbtx4937_arch_init NULL
-#endif /* CONFIG_PCI */
 
 static void toshiba_rbtx4927_restart(char *command)
 {
@@ -205,12 +223,6 @@ static void __init rbtx4927_mem_setup(void)
 #else
        set_io_port_base(KSEG1 + RBTX4927_ISA_IO_OFFSET);
 #endif
-
-       /* TX4927-SIO DTR on (PIO[15]) */
-       gpio_request(15, "sio-dtr");
-       gpio_direction_output(15, 1);
-
-       tx4927_sio_init(0, 0);
 }
 
 static void __init rbtx4927_clock_init(void)
index 54de66837103c702e341951b4aeb346e8bffc0f0..07939ed6b22fdac50316fbbfe702b8909effc6a0 100644 (file)
@@ -336,6 +336,7 @@ static void __init rbtx4938_mtd_init(void)
 
 static void __init rbtx4938_arch_init(void)
 {
+       txx9_gpio_init(TX4938_PIO_REG & 0xfffffffffULL, 0, TX4938_NUM_PIO);
        gpiochip_add_data(&rbtx4938_spi_gpio_chip, NULL);
        rbtx4938_pci_setup();
        rbtx4938_spi_init();
index 3b4538ec0102d5d28b15bc6421db11663fb377ff..c3dc12a8b7d9ddc79f8acf126cdd83bbb91dfee2 100644 (file)
@@ -13,8 +13,6 @@ cflags-vdso := $(ccflags-vdso) \
        -DDISABLE_BRANCH_PROFILING \
        $(call cc-option, -fno-stack-protector)
 aflags-vdso := $(ccflags-vdso) \
-       $(filter -I%,$(KBUILD_CFLAGS)) \
-       $(filter -E%,$(KBUILD_CFLAGS)) \
        -D__ASSEMBLY__ -Wa,-gdwarf-2
 
 #
@@ -82,7 +80,7 @@ obj-vdso := $(obj-vdso-y:%.o=$(obj)/%.o)
 $(obj-vdso): KBUILD_CFLAGS := $(cflags-vdso) $(native-abi)
 $(obj-vdso): KBUILD_AFLAGS := $(aflags-vdso) $(native-abi)
 
-$(obj)/vdso.lds: KBUILD_CPPFLAGS := $(native-abi)
+$(obj)/vdso.lds: KBUILD_CPPFLAGS := $(ccflags-vdso) $(native-abi)
 
 $(obj)/vdso.so.dbg.raw: $(obj)/vdso.lds $(obj-vdso) FORCE
        $(call if_changed,vdsold)
index 50d020ac0f487ef1427f02644748fe35a653960a..617dece6792427847458203c229505a054b5331e 100644 (file)
@@ -318,12 +318,12 @@ mpc85xx_smp_defconfig:
 PHONY += corenet32_smp_defconfig
 corenet32_smp_defconfig:
        $(call merge_into_defconfig,corenet_basic_defconfig,\
-               85xx-32bit 85xx-smp 85xx-hw fsl-emb-nonhw)
+               85xx-32bit 85xx-smp 85xx-hw fsl-emb-nonhw dpaa)
 
 PHONY += corenet64_smp_defconfig
 corenet64_smp_defconfig:
        $(call merge_into_defconfig,corenet_basic_defconfig,\
-               85xx-64bit 85xx-smp altivec 85xx-hw fsl-emb-nonhw)
+               85xx-64bit 85xx-smp altivec 85xx-hw fsl-emb-nonhw dpaa)
 
 PHONY += mpc86xx_defconfig
 mpc86xx_defconfig:
diff --git a/arch/powerpc/configs/dpaa.config b/arch/powerpc/configs/dpaa.config
new file mode 100644 (file)
index 0000000..efa99c0
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_FSL_DPAA=y
index ab9f4e0ed4cfcfd48a8d232fe20d0482739a22c5..5c4fbc80dc6ce683d278ce13174b770f337bcaa0 100644 (file)
@@ -1,5 +1,6 @@
 generic-y += clkdev.h
 generic-y += div64.h
+generic-y += export.h
 generic-y += irq_regs.h
 generic-y += irq_work.h
 generic-y += local64.h
index f752e6f7cfbe2656ab0ded7f6619b22339e973ae..ab68d0ee7725861d827d34c4f05ed08001e13274 100644 (file)
@@ -43,6 +43,7 @@ extern int machine_check_e500mc(struct pt_regs *regs);
 extern int machine_check_e500(struct pt_regs *regs);
 extern int machine_check_e200(struct pt_regs *regs);
 extern int machine_check_47x(struct pt_regs *regs);
+int machine_check_8xx(struct pt_regs *regs);
 
 extern void cpu_down_flush_e500v2(void);
 extern void cpu_down_flush_e500mc(void);
index c7d82ff62a3346eb6b807d0bbbd06a5c85a760e4..eba60416536ec0955ff4a4131b4d788db2f25f9c 100644 (file)
@@ -155,6 +155,8 @@ static inline unsigned long arch_local_irq_save(void)
        unsigned long flags = arch_local_save_flags();
 #ifdef CONFIG_BOOKE
        asm volatile("wrteei 0" : : : "memory");
+#elif defined(CONFIG_PPC_8xx)
+       wrtspr(SPRN_EID);
 #else
        SET_MSR_EE(flags & ~MSR_EE);
 #endif
@@ -165,6 +167,8 @@ static inline void arch_local_irq_disable(void)
 {
 #ifdef CONFIG_BOOKE
        asm volatile("wrteei 0" : : : "memory");
+#elif defined(CONFIG_PPC_8xx)
+       wrtspr(SPRN_EID);
 #else
        arch_local_irq_save();
 #endif
@@ -174,6 +178,8 @@ static inline void arch_local_irq_enable(void)
 {
 #ifdef CONFIG_BOOKE
        asm volatile("wrteei 1" : : : "memory");
+#elif defined(CONFIG_PPC_8xx)
+       wrtspr(SPRN_EIE);
 #else
        unsigned long msr = mfmsr();
        SET_MSR_EE(msr | MSR_EE);
index 4d8518049f4df569a24a10437afd3aff1c2df873..4396db57b8be19bbe12377c6c6babc6a99c6b6e8 100644 (file)
@@ -1,12 +1,8 @@
 #ifndef __ASM_POWERPC_LIBATA_PORTMAP_H
 #define __ASM_POWERPC_LIBATA_PORTMAP_H
 
-#define ATA_PRIMARY_CMD        0x1F0
-#define ATA_PRIMARY_CTL        0x3F6
 #define ATA_PRIMARY_IRQ(dev)   pci_get_legacy_ide_irq(dev, 0)
 
-#define ATA_SECONDARY_CMD      0x170
-#define ATA_SECONDARY_CTL      0x376
 #define ATA_SECONDARY_IRQ(dev) pci_get_legacy_ide_irq(dev, 1)
 
 #endif
index 54ff8ce7fa96b7fec908fd9c4a86dc94f024447c..0132831b3081934254b71e4f032d755a01a1d16a 100644 (file)
 #define PPC_INST_LWSYNC                        0x7c2004ac
 #define PPC_INST_SYNC                  0x7c0004ac
 #define PPC_INST_SYNC_MASK             0xfc0007fe
+#define PPC_INST_ISYNC                 0x4c00012c
 #define PPC_INST_LXVD2X                        0x7c000698
 #define PPC_INST_MCRXR                 0x7c000400
 #define PPC_INST_MCRXR_MASK            0xfc0007fe
index 2a620789954bbe3a21a93b8e9db7a031cb3077a3..9cd4e8cbc78c6ca6eac7948e0cf4458d0f827fcd 100644 (file)
@@ -1250,6 +1250,8 @@ static inline void mtmsr_isync(unsigned long val)
                                     : "r" ((unsigned long)(v)) \
                                     : "memory")
 #endif
+#define wrtspr(rn)     asm volatile("mtspr " __stringify(rn) ",0" : \
+                                    : : "memory")
 
 extern unsigned long msr_check_and_set(unsigned long bits);
 extern bool strict_msr_control;
index 94d01f81e66877646faba091187ae981bfa0a457..0197e12f7d482512c2d7be8f8ee76c1c3935df73 100644 (file)
 #define SPRN_MD_RAM0   825
 #define SPRN_MD_RAM1   826
 
+/* Special MSR manipulation registers */
+#define SPRN_EIE       80      /* External interrupt enable (EE=1, RI=1) */
+#define SPRN_EID       81      /* External interrupt disable (EE=0, RI=1) */
+
 /* Commands.  Only the first few are available to the instruction cache.
 */
 #define        IDC_ENABLE      0x02000000      /* Cache enable */
index aded29ad2e8f4873e06a01c1e580e47bbc181fcf..1925341dbb9c9df7ceb05975cf95c0ff8a3339a5 100644 (file)
@@ -14,6 +14,11 @@ CFLAGS_prom_init.o      += -fPIC
 CFLAGS_btext.o         += -fPIC
 endif
 
+CFLAGS_cputable.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
+CFLAGS_init.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
+CFLAGS_btext.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
+CFLAGS_prom.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
+
 ifdef CONFIG_FUNCTION_TRACER
 # Do not trace early boot code
 CFLAGS_REMOVE_cputable.o = -mno-sched-epilog $(CC_FLAGS_FTRACE)
@@ -90,10 +95,6 @@ obj-$(CONFIG_RELOCATABLE)    += reloc_$(BITS).o
 obj-$(CONFIG_PPC32)            += entry_32.o setup_32.o
 obj-$(CONFIG_PPC64)            += dma-iommu.o iommu.o
 obj-$(CONFIG_KGDB)             += kgdb.o
-obj-$(CONFIG_MODULES)          += ppc_ksyms.o
-ifeq ($(CONFIG_PPC32),y)
-obj-$(CONFIG_MODULES)          += ppc_ksyms_32.o
-endif
 obj-$(CONFIG_BOOTX_TEXT)       += btext.o
 obj-$(CONFIG_SMP)              += smp.o
 obj-$(CONFIG_KPROBES)          += kprobes.o
index 6c4646ac9234dafb186ca8a5360a3530b1e2940b..6a82ef039c509746f5afa66ebc286fa5da429ed3 100644 (file)
@@ -1248,6 +1248,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
                .mmu_features           = MMU_FTR_TYPE_8xx,
                .icache_bsize           = 16,
                .dcache_bsize           = 16,
+               .machine_check          = machine_check_8xx,
                .platform               = "ppc823",
        },
 #endif /* CONFIG_8xx */
index 83428a283fa075fcac6ec48676c44d2078427a6f..3841d749a430069f4d4f2705c4199c08609b3757 100644 (file)
@@ -33,6 +33,7 @@
 #include <asm/unistd.h>
 #include <asm/ftrace.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
 /*
  * MSR_KERNEL is > 0x10000 on 4xx/Book-E since it include MSR_CE.
@@ -1358,6 +1359,7 @@ _GLOBAL(_mcount)
        MCOUNT_RESTORE_FRAME
        bctr
 #endif
+EXPORT_SYMBOL(_mcount)
 
 _GLOBAL(ftrace_stub)
        blr
index 51df82b610843d5c490741fa33d59afb5a01c08b..6432d4bf08c889c128803cc5505546122118b964 100644 (file)
@@ -38,6 +38,7 @@
 #include <asm/context_tracking.h>
 #include <asm/tm.h>
 #include <asm/ppc-opcode.h>
+#include <asm/export.h>
 
 /*
  * System calls.
@@ -1177,6 +1178,7 @@ _GLOBAL(enter_prom)
 #ifdef CONFIG_DYNAMIC_FTRACE
 _GLOBAL(mcount)
 _GLOBAL(_mcount)
+EXPORT_SYMBOL(_mcount)
        mflr    r12
        mtctr   r12
        mtlr    r0
@@ -1413,6 +1415,7 @@ livepatch_handler:
 
 #else
 _GLOBAL_TOC(_mcount)
+EXPORT_SYMBOL(_mcount)
        /* Taken from output of objdump from lib64/glibc */
        mflr    r3
        ld      r11, 0(r1)
index 9f1ebf7338f1084499fcc03e50efa71b66a73b76..52ca2471ee1a4355b8fa3063bcc5d27ba61a52a1 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/ppc_asm.h>
 #include <asm/asm-compat.h>
 #include <asm/asm-offsets.h>
+#include <asm/export.h>
 
 #ifndef CONFIG_PPC64
 /* epapr_ev_idle() was derived from e500_idle() */
@@ -53,3 +54,4 @@ epapr_hypercall_start:
        nop
        nop
        blr
+EXPORT_SYMBOL(epapr_hypercall_start)
index 08992f8f50365612fd82d6be6cb2daebe3ea2918..f129408c602290d7ccf5870e2b12b46b0380bea5 100644 (file)
@@ -1377,7 +1377,7 @@ __end_interrupts:
 DEFINE_FIXED_SYMBOL(__end_interrupts)
 
 #ifdef CONFIG_PPC_970_NAP
-TRAMP_REAL_BEGIN(power4_fixup_nap)
+EXC_COMMON_BEGIN(power4_fixup_nap)
        andc    r9,r9,r10
        std     r9,TI_LOCAL_FLAGS(r11)
        ld      r10,_LINK(r1)           /* make idle task do the */
index 08d14b096eb90e8f9a0ff1c2de2a68c1ec2dd325..6c509f39bbdeb97c7c18a28e4479792a1d0fb7bf 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/ppc_asm.h>
 #include <asm/asm-offsets.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
 #ifdef CONFIG_VSX
 #define __REST_32FPVSRS(n,c,base)                                      \
@@ -59,6 +60,7 @@ _GLOBAL(load_fp_state)
        MTFSF_L(fr0)
        REST_32FPVSRS(0, R4, R3)
        blr
+EXPORT_SYMBOL(load_fp_state)
 
 /*
  * Store FP state into memory, including FPSCR
@@ -69,6 +71,7 @@ _GLOBAL(store_fp_state)
        mffs    fr0
        stfd    fr0,FPSTATE_FPSCR(r3)
        blr
+EXPORT_SYMBOL(store_fp_state)
 
 /*
  * This task wants to use the FPU now.
index a3f821eb7e9ab9985df07d3631d4dec2ecf03baf..9d963547d2438864d36662271912c4a2c28f21ef 100644 (file)
@@ -34,6 +34,7 @@
 #include <asm/ptrace.h>
 #include <asm/bug.h>
 #include <asm/kvm_book3s_asm.h>
+#include <asm/export.h>
 
 /* 601 only have IBAT; cr0.eq is set on 601 when using this macro */
 #define LOAD_BAT(n, reg, RA, RB)       \
@@ -738,6 +739,7 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_NEED_DTLB_SW_LRU)
 
        .globl mol_trampoline
        .set mol_trampoline, i0x2f00
+       EXPORT_SYMBOL(mol_trampoline)
 
        . = 0x3000
 
@@ -1045,6 +1047,7 @@ _ENTRY(switch_mmu_context)
 4:     trap
        EMIT_BUG_ENTRY 4b,__FILE__,__LINE__,0
        blr
+EXPORT_SYMBOL(switch_mmu_context)
 
 /*
  * An undocumented "feature" of 604e requires that the v bit
@@ -1272,6 +1275,7 @@ sdata:
        .globl  empty_zero_page
 empty_zero_page:
        .space  4096
+EXPORT_SYMBOL(empty_zero_page)
 
        .globl  swapper_pg_dir
 swapper_pg_dir:
@@ -1285,6 +1289,7 @@ intercept_table:
        .long 0, 0, 0, 0, 0, 0, 0, 0
        .long 0, 0, 0, 0, 0, 0, 0, 0
        .long 0, 0, 0, 0, 0, 0, 0, 0
+EXPORT_SYMBOL(intercept_table)
 
 /* Room for two PTE pointers, usually the kernel and current user pointers
  * to their respective root page table.
index 7d7d8635227ac76bcd054b3fd04248ec90127c9d..41374a468d1c1d49a801f6d0856ced3908b29137 100644 (file)
@@ -41,6 +41,7 @@
 #include <asm/ppc_asm.h>
 #include <asm/asm-offsets.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
 /* As with the other PowerPC ports, it is expected that when code
  * execution begins here, the following registers contain valid, yet
@@ -971,6 +972,7 @@ sdata:
        .globl  empty_zero_page
 empty_zero_page:
        .space  4096
+EXPORT_SYMBOL(empty_zero_page)
        .globl  swapper_pg_dir
 swapper_pg_dir:
        .space  PGD_TABLE_SIZE
index 9cdf5c71e4263c102a2294ffb251f991f98a8c03..37e4a7cf0065be285ba00b30a7601998a9e570f0 100644 (file)
@@ -39,6 +39,7 @@
 #include <asm/asm-offsets.h>
 #include <asm/ptrace.h>
 #include <asm/synch.h>
+#include <asm/export.h>
 #include "head_booke.h"
 
 
@@ -1254,6 +1255,7 @@ sdata:
        .globl  empty_zero_page
 empty_zero_page:
        .space  PAGE_SIZE
+EXPORT_SYMBOL(empty_zero_page)
 
 /*
  * To support >32-bit physical addresses, we use an 8KB pgdir.
index 79da0641bae24e5e9439df2a666f989b975a3896..04c546e20cc05e2db9c3f3022efcc4d2b9a91ee7 100644 (file)
@@ -43,6 +43,7 @@
 #include <asm/hw_irq.h>
 #include <asm/cputhreads.h>
 #include <asm/ppc-opcode.h>
+#include <asm/export.h>
 
 /* The physical memory is laid out such that the secondary processor
  * spin code sits at 0x0000...0x00ff. On server, the vectors follow
@@ -1002,3 +1003,4 @@ swapper_pg_dir:
        .globl  empty_zero_page
 empty_zero_page:
        .space  PAGE_SIZE
+EXPORT_SYMBOL(empty_zero_page)
index 3a185c51ce8f657d47709f12ffabb5e6480eff7f..fb133a1632636c5c252ddf222b05a1c4a6972896 100644 (file)
@@ -31,6 +31,7 @@
 #include <asm/asm-offsets.h>
 #include <asm/ptrace.h>
 #include <asm/fixmap.h>
+#include <asm/export.h>
 
 /* Macro to make the code more readable. */
 #ifdef CONFIG_8xx_CPU6
@@ -226,7 +227,7 @@ i##n:                                                               \
                          ret_from_except)
 
 /* System reset */
-       EXCEPTION(0x100, Reset, unknown_exception, EXC_XFER_STD)
+       EXCEPTION(0x100, Reset, system_reset_exception, EXC_XFER_STD)
 
 /* Machine check */
        . = 0x200
@@ -321,7 +322,7 @@ SystemCall:
 #endif
 
 InstructionTLBMiss:
-#ifdef CONFIG_8xx_CPU6
+#if defined(CONFIG_8xx_CPU6) || defined(CONFIG_MODULES) || defined (CONFIG_DEBUG_PAGEALLOC)
        mtspr   SPRN_SPRG_SCRATCH2, r3
 #endif
        EXCEPTION_PROLOG_0
@@ -329,23 +330,20 @@ InstructionTLBMiss:
        /* If we are faulting a kernel address, we have to use the
         * kernel page tables.
         */
+       mfspr   r10, SPRN_SRR0  /* Get effective address of fault */
+       INVALIDATE_ADJACENT_PAGES_CPU15(r11, r10)
 #if defined(CONFIG_MODULES) || defined (CONFIG_DEBUG_PAGEALLOC)
        /* Only modules will cause ITLB Misses as we always
         * pin the first 8MB of kernel memory */
-       mfspr   r11, SPRN_SRR0  /* Get effective address of fault */
-       INVALIDATE_ADJACENT_PAGES_CPU15(r10, r11)
-       mfcr    r10
-       IS_KERNEL(r11, r11)
+       mfcr    r3
+       IS_KERNEL(r11, r10)
+#endif
        mfspr   r11, SPRN_M_TW  /* Get level 1 table */
+#if defined(CONFIG_MODULES) || defined (CONFIG_DEBUG_PAGEALLOC)
        BRANCH_UNLESS_KERNEL(3f)
        lis     r11, (swapper_pg_dir-PAGE_OFFSET)@ha
 3:
-       mtcr    r10
-       mfspr   r10, SPRN_SRR0  /* Get effective address of fault */
-#else
-       mfspr   r10, SPRN_SRR0  /* Get effective address of fault */
-       INVALIDATE_ADJACENT_PAGES_CPU15(r11, r10)
-       mfspr   r11, SPRN_M_TW  /* Get level 1 table base address */
+       mtcr    r3
 #endif
        /* Insert level 1 index */
        rlwimi  r11, r10, 32 - ((PAGE_SHIFT - 2) << 1), (PAGE_SHIFT - 2) << 1, 29
@@ -377,58 +375,39 @@ InstructionTLBMiss:
        MTSPR_CPU6(SPRN_MI_RPN, r10, r3)        /* Update TLB entry */
 
        /* Restore registers */
-#ifdef CONFIG_8xx_CPU6
+#if defined(CONFIG_8xx_CPU6) || defined(CONFIG_MODULES) || defined (CONFIG_DEBUG_PAGEALLOC)
        mfspr   r3, SPRN_SPRG_SCRATCH2
 #endif
        EXCEPTION_EPILOG_0
        rfi
 
-/*
- * Bottom part of DataStoreTLBMiss handler for IMMR area
- * not enough space in the DataStoreTLBMiss area
- */
-DTLBMissIMMR:
-       mtcr    r10
-       /* Set 512k byte guarded page and mark it valid */
-       li      r10, MD_PS512K | MD_GUARDED | MD_SVALID
-       MTSPR_CPU6(SPRN_MD_TWC, r10, r11)
-       mfspr   r10, SPRN_IMMR                  /* Get current IMMR */
-       rlwinm  r10, r10, 0, 0xfff80000         /* Get 512 kbytes boundary */
-       ori     r10, r10, 0xf0 | MD_SPS16K | _PAGE_SHARED | _PAGE_DIRTY | \
-                         _PAGE_PRESENT | _PAGE_NO_CACHE
-       MTSPR_CPU6(SPRN_MD_RPN, r10, r11)       /* Update TLB entry */
-
-       li      r11, RPN_PATTERN
-       mtspr   SPRN_DAR, r11   /* Tag DAR */
-       EXCEPTION_EPILOG_0
-       rfi
-
        . = 0x1200
 DataStoreTLBMiss:
+       mtspr   SPRN_SPRG_SCRATCH2, r3
        EXCEPTION_PROLOG_0
-       mfcr    r10
+       mfcr    r3
 
        /* If we are faulting a kernel address, we have to use the
         * kernel page tables.
         */
-       mfspr   r11, SPRN_MD_EPN
-       rlwinm  r11, r11, 16, 0xfff8
+       mfspr   r10, SPRN_MD_EPN
+       rlwinm  r10, r10, 16, 0xfff8
+       cmpli   cr0, r10, PAGE_OFFSET@h
+       mfspr   r11, SPRN_M_TW  /* Get level 1 table */
+       blt+    3f
 #ifndef CONFIG_PIN_TLB_IMMR
-       cmpli   cr0, r11, VIRT_IMMR_BASE@h
+       cmpli   cr0, r10, VIRT_IMMR_BASE@h
 #endif
-       cmpli   cr7, r11, PAGE_OFFSET@h
+_ENTRY(DTLBMiss_cmp)
+       cmpli   cr7, r10, (PAGE_OFFSET + 0x1800000)@h
+       lis     r11, (swapper_pg_dir-PAGE_OFFSET)@ha
 #ifndef CONFIG_PIN_TLB_IMMR
 _ENTRY(DTLBMiss_jmp)
        beq-    DTLBMissIMMR
 #endif
-       bge-    cr7, 4f
-
-       mfspr   r11, SPRN_M_TW  /* Get level 1 table */
+       blt     cr7, DTLBMissLinear
 3:
-       mtcr    r10
-#ifdef CONFIG_8xx_CPU6
-       mtspr   SPRN_SPRG_SCRATCH2, r3
-#endif
+       mtcr    r3
        mfspr   r10, SPRN_MD_EPN
 
        /* Insert level 1 index */
@@ -481,30 +460,7 @@ _ENTRY(DTLBMiss_jmp)
        MTSPR_CPU6(SPRN_MD_RPN, r10, r3)        /* Update TLB entry */
 
        /* Restore registers */
-#ifdef CONFIG_8xx_CPU6
        mfspr   r3, SPRN_SPRG_SCRATCH2
-#endif
-       mtspr   SPRN_DAR, r11   /* Tag DAR */
-       EXCEPTION_EPILOG_0
-       rfi
-
-4:
-_ENTRY(DTLBMiss_cmp)
-       cmpli   cr0, r11, (PAGE_OFFSET + 0x1800000)@h
-       lis     r11, (swapper_pg_dir-PAGE_OFFSET)@ha
-       bge-    3b
-
-       mtcr    r10
-       /* Set 8M byte page and mark it valid */
-       li      r10, MD_PS8MEG | MD_SVALID
-       MTSPR_CPU6(SPRN_MD_TWC, r10, r11)
-       mfspr   r10, SPRN_MD_EPN
-       rlwinm  r10, r10, 0, 0x0f800000         /* 8xx supports max 256Mb RAM */
-       ori     r10, r10, 0xf0 | MD_SPS16K | _PAGE_SHARED | _PAGE_DIRTY | \
-                         _PAGE_PRESENT
-       MTSPR_CPU6(SPRN_MD_RPN, r10, r11)       /* Update TLB entry */
-
-       li      r11, RPN_PATTERN
        mtspr   SPRN_DAR, r11   /* Tag DAR */
        EXCEPTION_EPILOG_0
        rfi
@@ -570,6 +526,43 @@ DARFixed:/* Return from dcbx instruction bug workaround */
 
        . = 0x2000
 
+/*
+ * Bottom part of DataStoreTLBMiss handlers for IMMR area and linear RAM.
+ * not enough space in the DataStoreTLBMiss area.
+ */
+DTLBMissIMMR:
+       mtcr    r3
+       /* Set 512k byte guarded page and mark it valid */
+       li      r10, MD_PS512K | MD_GUARDED | MD_SVALID
+       MTSPR_CPU6(SPRN_MD_TWC, r10, r11)
+       mfspr   r10, SPRN_IMMR                  /* Get current IMMR */
+       rlwinm  r10, r10, 0, 0xfff80000         /* Get 512 kbytes boundary */
+       ori     r10, r10, 0xf0 | MD_SPS16K | _PAGE_SHARED | _PAGE_DIRTY | \
+                         _PAGE_PRESENT | _PAGE_NO_CACHE
+       MTSPR_CPU6(SPRN_MD_RPN, r10, r11)       /* Update TLB entry */
+
+       li      r11, RPN_PATTERN
+       mtspr   SPRN_DAR, r11   /* Tag DAR */
+       mfspr   r3, SPRN_SPRG_SCRATCH2
+       EXCEPTION_EPILOG_0
+       rfi
+
+DTLBMissLinear:
+       mtcr    r3
+       /* Set 8M byte page and mark it valid */
+       li      r11, MD_PS8MEG | MD_SVALID
+       MTSPR_CPU6(SPRN_MD_TWC, r11, r3)
+       rlwinm  r10, r10, 16, 0x0f800000        /* 8xx supports max 256Mb RAM */
+       ori     r10, r10, 0xf0 | MD_SPS16K | _PAGE_SHARED | _PAGE_DIRTY | \
+                         _PAGE_PRESENT
+       MTSPR_CPU6(SPRN_MD_RPN, r10, r11)       /* Update TLB entry */
+
+       li      r11, RPN_PATTERN
+       mtspr   SPRN_DAR, r11   /* Tag DAR */
+       mfspr   r3, SPRN_SPRG_SCRATCH2
+       EXCEPTION_EPILOG_0
+       rfi
+
 /* This is the procedure to calculate the data EA for buggy dcbx,dcbi instructions
  * by decoding the registers used by the dcbx instruction and adding them.
  * DAR is set to the calculated address.
@@ -586,7 +579,9 @@ FixupDAR:/* Entry point for dcbx workaround. */
        rlwinm  r11, r10, 16, 0xfff8
 _ENTRY(FixupDAR_cmp)
        cmpli   cr7, r11, (PAGE_OFFSET + 0x1800000)@h
-       blt-    cr7, 200f
+       /* create physical page address from effective address */
+       tophys(r11, r10)
+       blt-    cr7, 201f
        lis     r11, (swapper_pg_dir-PAGE_OFFSET)@ha
        /* Insert level 1 index */
 3:     rlwimi  r11, r10, 32 - ((PAGE_SHIFT - 2) << 1), (PAGE_SHIFT - 2) << 1, 29
@@ -616,10 +611,6 @@ _ENTRY(FixupDAR_cmp)
 141:   mfspr   r10,SPRN_SPRG_SCRATCH2
        b       DARFixed        /* Nope, go back to normal TLB processing */
 
-       /* create physical page address from effective address */
-200:   tophys(r11, r10)
-       b       201b
-
 144:   mfspr   r10, SPRN_DSISR
        rlwinm  r10, r10,0,7,5  /* Clear store bit for buggy dcbst insn */
        mtspr   SPRN_DSISR, r10
@@ -894,6 +885,7 @@ sdata:
        .align  PAGE_SHIFT
 empty_zero_page:
        .space  PAGE_SIZE
+EXPORT_SYMBOL(empty_zero_page)
 
        .globl  swapper_pg_dir
 swapper_pg_dir:
index 3bfa3150911f7b42f20b7bfb7e7715be6c10871e..bf4c6021515f8aedff5a8c2cfc39f37b39850982 100644 (file)
@@ -42,6 +42,7 @@
 #include <asm/asm-offsets.h>
 #include <asm/cache.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 #include "head_booke.h"
 
 /* As with the other PowerPC ports, it is expected that when code
@@ -1223,6 +1224,7 @@ sdata:
        .globl  empty_zero_page
 empty_zero_page:
        .space  4096
+EXPORT_SYMBOL(empty_zero_page)
        .globl  swapper_pg_dir
 swapper_pg_dir:
        .space  PGD_TABLE_SIZE
index 0d432194c01825286b4f629ad1637cdbeed32a22..384357cb8bc0031170e764549c1a493745b5b3af 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/unistd.h>
 #include <asm/asm-compat.h>
 #include <asm/asm-offsets.h>
+#include <asm/export.h>
 
        .text
 
@@ -118,3 +119,4 @@ _GLOBAL(longjmp)
 _GLOBAL(current_stack_pointer)
        PPC_LL  r3,0(r1)
        blr
+EXPORT_SYMBOL(current_stack_pointer)
index 03756ffdcd71063dd028d7d3825d89d70e47990c..93cf7a5846a6f5875534cc43a2a3409357dc482a 100644 (file)
@@ -33,6 +33,7 @@
 #include <asm/kexec.h>
 #include <asm/bug.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
        .text
 
@@ -319,6 +320,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_UNIFIED_ID_CACHE)
 #endif /* CONFIG_4xx */
        isync
        blr
+EXPORT_SYMBOL(flush_instruction_cache)
 #endif /* CONFIG_PPC_8xx */
 
 /*
@@ -359,6 +361,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE)
        isync
        blr
 _ASM_NOKPROBE_SYMBOL(flush_icache_range)
+EXPORT_SYMBOL(flush_icache_range)
 
 /*
  * Flush a particular page from the data cache to RAM.
@@ -497,6 +500,7 @@ _GLOBAL(copy_page)
        li      r0,MAX_COPY_PREFETCH
        li      r11,4
        b       2b
+EXPORT_SYMBOL(copy_page)
 
 /*
  * Extended precision shifts.
@@ -524,6 +528,7 @@ _GLOBAL(__ashrdi3)
        sraw    r3,r3,r5        # MSW = MSW >> count
        or      r4,r4,r7        # LSW |= t2
        blr
+EXPORT_SYMBOL(__ashrdi3)
 
 _GLOBAL(__ashldi3)
        subfic  r6,r5,32
@@ -535,6 +540,7 @@ _GLOBAL(__ashldi3)
        slw     r4,r4,r5        # LSW = LSW << count
        or      r3,r3,r7        # MSW |= t2
        blr
+EXPORT_SYMBOL(__ashldi3)
 
 _GLOBAL(__lshrdi3)
        subfic  r6,r5,32
@@ -546,6 +552,7 @@ _GLOBAL(__lshrdi3)
        srw     r3,r3,r5        # MSW = MSW >> count
        or      r4,r4,r7        # LSW |= t2
        blr
+EXPORT_SYMBOL(__lshrdi3)
 
 /*
  * 64-bit comparison: __cmpdi2(s64 a, s64 b)
@@ -561,6 +568,7 @@ _GLOBAL(__cmpdi2)
        bltlr
        li      r3,2
        blr
+EXPORT_SYMBOL(__cmpdi2)
 /*
  * 64-bit comparison: __ucmpdi2(u64 a, u64 b)
  * Returns 0 if a < b, 1 if a == b, 2 if a > b.
@@ -575,6 +583,7 @@ _GLOBAL(__ucmpdi2)
        bltlr
        li      r3,2
        blr
+EXPORT_SYMBOL(__ucmpdi2)
 
 _GLOBAL(__bswapdi2)
        rotlwi  r9,r4,8
@@ -586,6 +595,7 @@ _GLOBAL(__bswapdi2)
        mr      r3,r9
        mr      r4,r10
        blr
+EXPORT_SYMBOL(__bswapdi2)
 
 #ifdef CONFIG_SMP
 _GLOBAL(start_secondary_resume)
index 9f0bed214bcb891d48078cfa094c3c1792a3a0d3..4f178671f230ccd799a9089ca4389590e94bc703 100644 (file)
@@ -27,6 +27,7 @@
 #include <asm/kexec.h>
 #include <asm/ptrace.h>
 #include <asm/mmu.h>
+#include <asm/export.h>
 
        .text
 
@@ -110,6 +111,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE)
        isync
        blr
 _ASM_NOKPROBE_SYMBOL(flush_icache_range)
+EXPORT_SYMBOL(flush_icache_range)
 
 /*
  * Like above, but only do the D-cache.
@@ -140,6 +142,7 @@ _GLOBAL(flush_dcache_range)
        bdnz    0b
        sync
        blr
+EXPORT_SYMBOL(flush_dcache_range)
 
 /*
  * Like above, but works on non-mapped physical addresses.
@@ -243,6 +246,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE)
        blr
 
 _GLOBAL(__bswapdi2)
+EXPORT_SYMBOL(__bswapdi2)
        srdi    r8,r3,32
        rlwinm  r7,r3,8,0xffffffff
        rlwimi  r7,r3,24,0,7
index 95d3769a2e267cd9ee14b1901418c74526ceb975..74bec549897202e797b2f0c7de30ad7d83ed9cfb 100644 (file)
@@ -56,6 +56,7 @@ static DECLARE_BITMAP(phb_bitmap, MAX_PHBS);
 
 /* ISA Memory physical address */
 resource_size_t isa_mem_base;
+EXPORT_SYMBOL(isa_mem_base);
 
 
 static struct dma_map_ops *pci_dma_ops = &dma_direct_ops;
index 1f7930037cb7df036ad61d4358bc29fab994618f..678f87a63645718e0f63488f52ff5ae6fc72ae06 100644 (file)
@@ -32,6 +32,8 @@
 unsigned long isa_io_base     = 0;
 unsigned long pci_dram_offset = 0;
 int pcibios_assign_bus_offset = 1;
+EXPORT_SYMBOL(isa_io_base);
+EXPORT_SYMBOL(pci_dram_offset);
 
 void pcibios_make_OF_bus_map(void);
 
diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c
deleted file mode 100644 (file)
index 9f01e28..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#include <linux/ftrace.h>
-#include <linux/mm.h>
-
-#include <asm/processor.h>
-#include <asm/switch_to.h>
-#include <asm/cacheflush.h>
-#include <asm/epapr_hcalls.h>
-
-#ifdef CONFIG_PPC64
-EXPORT_SYMBOL(flush_dcache_range);
-#endif
-EXPORT_SYMBOL(flush_icache_range);
-
-EXPORT_SYMBOL(empty_zero_page);
-
-long long __bswapdi2(long long);
-EXPORT_SYMBOL(__bswapdi2);
-
-#ifdef CONFIG_FUNCTION_TRACER
-EXPORT_SYMBOL(_mcount);
-#endif
-
-#ifdef CONFIG_PPC_FPU
-EXPORT_SYMBOL(load_fp_state);
-EXPORT_SYMBOL(store_fp_state);
-#endif
-
-#ifdef CONFIG_ALTIVEC
-EXPORT_SYMBOL(load_vr_state);
-EXPORT_SYMBOL(store_vr_state);
-#endif
-
-#ifdef CONFIG_EPAPR_PARAVIRT
-EXPORT_SYMBOL(epapr_hypercall_start);
-#endif
-
-EXPORT_SYMBOL(current_stack_pointer);
diff --git a/arch/powerpc/kernel/ppc_ksyms_32.c b/arch/powerpc/kernel/ppc_ksyms_32.c
deleted file mode 100644 (file)
index 2bfaafe..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#include <linux/export.h>
-#include <linux/smp.h>
-
-#include <asm/page.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/hw_irq.h>
-#include <asm/time.h>
-#include <asm/mmu_context.h>
-#include <asm/pgtable.h>
-#include <asm/dcr.h>
-
-EXPORT_SYMBOL(ISA_DMA_THRESHOLD);
-EXPORT_SYMBOL(DMA_MODE_READ);
-EXPORT_SYMBOL(DMA_MODE_WRITE);
-
-#if defined(CONFIG_PCI)
-EXPORT_SYMBOL(isa_io_base);
-EXPORT_SYMBOL(isa_mem_base);
-EXPORT_SYMBOL(pci_dram_offset);
-#endif
-
-#ifdef CONFIG_SMP
-EXPORT_SYMBOL(smp_hw_index);
-#endif
-
-long long __ashrdi3(long long, int);
-long long __ashldi3(long long, int);
-long long __lshrdi3(long long, int);
-int __ucmpdi2(unsigned long long, unsigned long long);
-int __cmpdi2(long long, long long);
-EXPORT_SYMBOL(__ashrdi3);
-EXPORT_SYMBOL(__ashldi3);
-EXPORT_SYMBOL(__lshrdi3);
-EXPORT_SYMBOL(__ucmpdi2);
-EXPORT_SYMBOL(__cmpdi2);
-
-EXPORT_SYMBOL(timer_interrupt);
-EXPORT_SYMBOL(tb_ticks_per_jiffy);
-
-EXPORT_SYMBOL(switch_mmu_context);
-
-#ifdef CONFIG_PPC_STD_MMU_32
-extern long mol_trampoline;
-EXPORT_SYMBOL(mol_trampoline); /* For MOL */
-EXPORT_SYMBOL(flush_hash_pages); /* For MOL */
-#ifdef CONFIG_SMP
-extern int mmu_hash_lock;
-EXPORT_SYMBOL(mmu_hash_lock); /* For MOL */
-#endif /* CONFIG_SMP */
-extern long *intercept_table;
-EXPORT_SYMBOL(intercept_table);
-#endif /* CONFIG_PPC_STD_MMU_32 */
-
-#ifdef CONFIG_PPC_DCR_NATIVE
-EXPORT_SYMBOL(__mtdcr);
-EXPORT_SYMBOL(__mfdcr);
-#endif
-
-EXPORT_SYMBOL(flush_instruction_cache);
index f52b7db327c80a3b603fb2a7179a2f97d6a054be..010b7b310237e4be38ef1d7bcc15a76050fc4469 100644 (file)
@@ -74,7 +74,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
                        break;
 
                copied = access_process_vm(child, (u64)addrOthers, &tmp,
-                               sizeof(tmp), 0);
+                               sizeof(tmp), FOLL_FORCE);
                if (copied != sizeof(tmp))
                        break;
                ret = put_user(tmp, (u32 __user *)data);
@@ -179,7 +179,8 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
                        break;
                ret = 0;
                if (access_process_vm(child, (u64)addrOthers, &tmp,
-                                       sizeof(tmp), 1) == sizeof(tmp))
+                                       sizeof(tmp),
+                                       FOLL_FORCE | FOLL_WRITE) == sizeof(tmp))
                        break;
                ret = -EIO;
                break;
index dba265c586df010fea218aa2c3d54102c5dbb708..270ee30abdcf739982438271d8b5957e7e830dbb 100644 (file)
@@ -131,15 +131,26 @@ void machine_shutdown(void)
                ppc_md.machine_shutdown();
 }
 
+static void machine_hang(void)
+{
+       pr_emerg("System Halted, OK to turn off power\n");
+       local_irq_disable();
+       while (1)
+               ;
+}
+
 void machine_restart(char *cmd)
 {
        machine_shutdown();
        if (ppc_md.restart)
                ppc_md.restart(cmd);
+
        smp_send_stop();
-       printk(KERN_EMERG "System Halted, OK to turn off power\n");
-       local_irq_disable();
-       while (1) ;
+
+       do_kernel_restart(cmd);
+       mdelay(1000);
+
+       machine_hang();
 }
 
 void machine_power_off(void)
@@ -147,10 +158,9 @@ void machine_power_off(void)
        machine_shutdown();
        if (pm_power_off)
                pm_power_off();
+
        smp_send_stop();
-       printk(KERN_EMERG "System Halted, OK to turn off power\n");
-       local_irq_disable();
-       while (1) ;
+       machine_hang();
 }
 /* Used by the G5 thermal driver */
 EXPORT_SYMBOL_GPL(machine_power_off);
@@ -163,10 +173,9 @@ void machine_halt(void)
        machine_shutdown();
        if (ppc_md.halt)
                ppc_md.halt();
+
        smp_send_stop();
-       printk(KERN_EMERG "System Halted, OK to turn off power\n");
-       local_irq_disable();
-       while (1) ;
+       machine_hang();
 }
 
 
index 24ec3ea4b3a2eeeeae2e0f713226acd252bab806..5fe79182f0fac97ef292b3ab680f7bc4cdd419e1 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/cpu.h>
 #include <linux/console.h>
 #include <linux/memblock.h>
+#include <linux/export.h>
 
 #include <asm/io.h>
 #include <asm/prom.h>
@@ -47,11 +48,16 @@ int boot_cpuid_phys;
 EXPORT_SYMBOL_GPL(boot_cpuid_phys);
 
 int smp_hw_index[NR_CPUS];
+EXPORT_SYMBOL(smp_hw_index);
 
 unsigned long ISA_DMA_THRESHOLD;
 unsigned int DMA_MODE_READ;
 unsigned int DMA_MODE_WRITE;
 
+EXPORT_SYMBOL(ISA_DMA_THRESHOLD);
+EXPORT_SYMBOL(DMA_MODE_READ);
+EXPORT_SYMBOL(DMA_MODE_WRITE);
+
 /*
  * These are used in binfmt_elf.c to put aux entries on the stack
  * for each elf executable being started.
index 67859b7d1c97b483a7487aedb838843911591ff4..bc3f7d0d7b7987dec3a74e4c59c2d133adb1330c 100644 (file)
@@ -596,6 +596,7 @@ void timer_interrupt(struct pt_regs * regs)
        irq_exit();
        set_irq_regs(old_regs);
 }
+EXPORT_SYMBOL(timer_interrupt);
 
 /*
  * Hypervisor decrementer interrupts shouldn't occur but are sometimes
index a1f8f5641e9e22db73c6f3a2ffc124605d1e2bfe..023a462725b5dccf39ff51521118986f824afdbe 100644 (file)
@@ -273,7 +273,6 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
        force_sig_info(signr, &info, current);
 }
 
-#ifdef CONFIG_PPC64
 void system_reset_exception(struct pt_regs *regs)
 {
        /* See if any machine dependent calls */
@@ -291,6 +290,7 @@ void system_reset_exception(struct pt_regs *regs)
        /* What should we do here? We could issue a shutdown or hard reset. */
 }
 
+#ifdef CONFIG_PPC64
 /*
  * This function is called in real mode. Strictly no printk's please.
  *
@@ -352,12 +352,11 @@ static inline int check_io_access(struct pt_regs *regs)
                 * For the debug message, we look at the preceding
                 * load or store.
                 */
-               if (*nip == 0x60000000)         /* nop */
+               if (*nip == PPC_INST_NOP)
                        nip -= 2;
-               else if (*nip == 0x4c00012c)    /* isync */
+               else if (*nip == PPC_INST_ISYNC)
                        --nip;
-               if (*nip == 0x7c0004ac || (*nip >> 26) == 3) {
-                       /* sync or twi */
+               if (*nip == PPC_INST_SYNC || (*nip >> 26) == OP_TRAP) {
                        unsigned int rb;
 
                        --nip;
@@ -668,6 +667,31 @@ int machine_check_e200(struct pt_regs *regs)
 
        return 0;
 }
+#elif defined(CONFIG_PPC_8xx)
+int machine_check_8xx(struct pt_regs *regs)
+{
+       unsigned long reason = get_mc_reason(regs);
+
+       pr_err("Machine check in kernel mode.\n");
+       pr_err("Caused by (from SRR1=%lx): ", reason);
+       if (reason & 0x40000000)
+               pr_err("Fetch error at address %lx\n", regs->nip);
+       else
+               pr_err("Data access error at address %lx\n", regs->dar);
+
+#ifdef CONFIG_PCI
+       /* the qspan pci read routines can cause machine checks -- Cort
+        *
+        * yuck !!! that totally needs to go away ! There are better ways
+        * to deal with that than having a wart in the mcheck handler.
+        * -- BenH
+        */
+       bad_page_fault(regs, regs->dar, SIGBUS);
+       return 1;
+#else
+       return 0;
+#endif
+}
 #else
 int machine_check_generic(struct pt_regs *regs)
 {
@@ -727,17 +751,6 @@ void machine_check_exception(struct pt_regs *regs)
        if (recover > 0)
                goto bail;
 
-#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
-       /* the qspan pci read routines can cause machine checks -- Cort
-        *
-        * yuck !!! that totally needs to go away ! There are better ways
-        * to deal with that than having a wart in the mcheck handler.
-        * -- BenH
-        */
-       bad_page_fault(regs, regs->dar, SIGBUS);
-       goto bail;
-#endif
-
        if (debugger_fault_handler(regs))
                goto bail;
 
index bc85bdff4e01a18ed7b241faf2b319b845ac13b6..0c123f3406cd0b19fc9bd8f08f6b6aeecd7235da 100644 (file)
@@ -6,6 +6,7 @@
 #include <asm/thread_info.h>
 #include <asm/page.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
 /*
  * Load state from memory into VMX registers including VSCR.
@@ -17,6 +18,7 @@ _GLOBAL(load_vr_state)
        mtvscr  v0
        REST_32VRS(0,r4,r3)
        blr
+EXPORT_SYMBOL(load_vr_state)
 
 /*
  * Store VMX state into memory, including VSCR.
@@ -28,6 +30,7 @@ _GLOBAL(store_vr_state)
        li      r4, VRSTATE_VSCR
        stvx    v0, r4, r3
        blr
+EXPORT_SYMBOL(store_vr_state)
 
 /*
  * Disable VMX for the task which had it previously,
index ad5290005ca432549e8b12f268845bb90834ca76..309361e8652331b65e9bc2d28b870f9791b6d986 100644 (file)
@@ -9,7 +9,7 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
 CFLAGS_REMOVE_code-patching.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_feature-fixups.o = $(CC_FLAGS_FTRACE)
 
-obj-y += string.o alloc.o crtsavres.o ppc_ksyms.o code-patching.o \
+obj-y += string.o alloc.o crtsavres.o code-patching.o \
         feature-fixups.o
 
 obj-$(CONFIG_PPC32)    += div64.o copy_32.o
index aa8214f30c920e05c6bf62349c6ea52e0ca68dc9..ea29a5d67743722dee2ca0f7fb15c98ad4b01e05 100644 (file)
@@ -17,6 +17,7 @@
 #include <asm/cache.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
        .text
 
@@ -68,6 +69,7 @@ _GLOBAL(__csum_partial)
        adde    r5,r5,r0
 5:     addze   r3,r5           /* add in final carry */
        blr
+EXPORT_SYMBOL(__csum_partial)
 
 /*
  * Computes the checksum of a memory block at src, length len,
@@ -297,3 +299,4 @@ dst_error:
        .long   41b,dst_error
        .long   50b,src_error
        .long   51b,dst_error
+EXPORT_SYMBOL(csum_partial_copy_generic)
index fdec6e613e954b8fa70f8f27a3971e378b1d6d1b..fd9176671f9fb41b1c74f866445bb2e52d29c6b9 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/processor.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 /*
  * Computes the checksum of a memory block at buff, length len,
@@ -176,6 +177,7 @@ _GLOBAL(__csum_partial)
        add     r3,r4,r0
        srdi    r3,r3,32
        blr
+EXPORT_SYMBOL(__csum_partial)
 
 
        .macro srcnr
@@ -430,3 +432,4 @@ dstnr;      stb     r6,0(r4)
        li      r6,-EFAULT
        stw     r6,0(r8)
        blr
+EXPORT_SYMBOL(csum_partial_copy_generic)
index 99f37f24185ca890127b3e2a1ebed7ed14f4ca55..40cce33b08d6a226f64ab94788725647c15df54d 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/cache.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 #define COPY_16_BYTES          \
        lwz     r7,4(r4);       \
@@ -92,6 +93,7 @@ _GLOBAL(memset)
        subf    r6,r0,r6
        cmplwi  0,r4,0
        bne     2f      /* Use normal procedure if r4 is not zero */
+EXPORT_SYMBOL(memset)
 _GLOBAL(memset_nocache_branch)
        b       2f      /* Skip optimised bloc until cache is enabled */
 
@@ -216,6 +218,8 @@ _GLOBAL(memcpy)
        stbu    r0,1(r6)
        bdnz    40b
 65:    blr
+EXPORT_SYMBOL(memcpy)
+EXPORT_SYMBOL(memmove)
 
 generic_memcpy:
        srwi.   r7,r5,3
@@ -507,3 +511,4 @@ _GLOBAL(__copy_tofrom_user)
        .long   112b,120b
        .long   114b,120b
        .text
+EXPORT_SYMBOL(__copy_tofrom_user)
index a3c4dc4defdd86e02ad098fa0269c68170935e78..21367b3a81465ea65ec38387d1e46e33cacbeeb6 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/processor.h>
 #include <asm/ppc_asm.h>
 #include <asm/asm-offsets.h>
+#include <asm/export.h>
 
         .section        ".toc","aw"
 PPC64_CACHES:
@@ -110,3 +111,4 @@ END_FTR_SECTION_IFSET(CPU_FTR_CP_USE_DCBTZ)
        std     r11,120(r3)
        std     r12,128(r3)
        blr
+EXPORT_SYMBOL(copy_page)
index f09899e35991711d0a57e74519af662b59e59f15..60386b2c99bb301165182e31bd876583f9c3b887 100644 (file)
@@ -8,6 +8,7 @@
  */
 #include <asm/processor.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 #ifdef __BIG_ENDIAN__
 #define sLd sld                /* Shift towards low-numbered address. */
@@ -359,6 +360,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
        addi    r3,r3,8
 171:
 177:
+179:
        addi    r3,r3,8
 370:
 372:
@@ -373,7 +375,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
 173:
 174:
 175:
-179:
 181:
 184:
 186:
@@ -671,3 +672,4 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
        .llong  89b,100b
        .llong  90b,100b
        .llong  91b,100b
+EXPORT_SYMBOL(__copy_tofrom_user)
index 19e66001a4f9d5ab6b1c1e7cc6f15c9b50b83b55..3de7ac154f24e7c1198ffdfc6942ff011088162f 100644 (file)
@@ -19,6 +19,7 @@
  */
 #include <asm/processor.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 /* Note: This code relies on -mminimal-toc */
 
@@ -32,6 +33,7 @@ FTR_SECTION_ELSE
        clrldi  r3,r3,64-8
        blr
 ALT_FTR_SECTION_END_IFCLR(CPU_FTR_POPCNTB)
+EXPORT_SYMBOL(__arch_hweight8)
 
 _GLOBAL(__arch_hweight16)
 BEGIN_FTR_SECTION
@@ -54,6 +56,7 @@ FTR_SECTION_ELSE
        blr
   ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_POPCNTD, 50)
 ALT_FTR_SECTION_END_IFCLR(CPU_FTR_POPCNTB)
+EXPORT_SYMBOL(__arch_hweight16)
 
 _GLOBAL(__arch_hweight32)
 BEGIN_FTR_SECTION
@@ -79,6 +82,7 @@ FTR_SECTION_ELSE
        blr
   ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_POPCNTD, 51)
 ALT_FTR_SECTION_END_IFCLR(CPU_FTR_POPCNTB)
+EXPORT_SYMBOL(__arch_hweight32)
 
 _GLOBAL(__arch_hweight64)
 BEGIN_FTR_SECTION
@@ -108,3 +112,4 @@ FTR_SECTION_ELSE
        blr
   ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_POPCNTD, 52)
 ALT_FTR_SECTION_END_IFCLR(CPU_FTR_POPCNTB)
+EXPORT_SYMBOL(__arch_hweight64)
index eda7a96161ab6f494e6403cc6fa5cc2da89cd4c9..85fa9869aec5848d1c22c7b24e297cb51f1d4a9d 100644 (file)
@@ -11,6 +11,7 @@
 #include <asm/processor.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 _GLOBAL(memset)
        neg     r0,r3
@@ -77,6 +78,7 @@ _GLOBAL(memset)
 10:    bflr    31
        stb     r4,0(r6)
        blr
+EXPORT_SYMBOL(memset)
 
 _GLOBAL_TOC(memmove)
        cmplw   0,r3,r4
@@ -119,3 +121,4 @@ _GLOBAL(backwards_memcpy)
        beq     2b
        mtctr   r7
        b       1b
+EXPORT_SYMBOL(memmove)
index 8953d2382a653a948afb340304b07bbfc51181d8..d75d18b7bd554d3d389d543d3084e43be29f1d16 100644 (file)
@@ -8,6 +8,7 @@
  * 2 of the License, or (at your option) any later version.
  */
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 #define off8   r6
 #define off16  r7
@@ -231,3 +232,4 @@ _GLOBAL(memcmp)
        ld      r28,-32(r1)
        ld      r27,-40(r1)
        blr
+EXPORT_SYMBOL(memcmp)
index 32a06ec395d2108202762c6d164b3994ca6a7644..f4d6088e2d5390b937f60e35819496dfeab17858 100644 (file)
@@ -8,6 +8,7 @@
  */
 #include <asm/processor.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
        .align  7
 _GLOBAL_TOC(memcpy)
@@ -219,3 +220,4 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
 4:     ld      r3,-STACKFRAMESIZE+STK_REG(R31)(r1)     /* return dest pointer */
        blr
 #endif
+EXPORT_SYMBOL(memcpy)
diff --git a/arch/powerpc/lib/ppc_ksyms.c b/arch/powerpc/lib/ppc_ksyms.c
deleted file mode 100644 (file)
index ae69d84..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <linux/string.h>
-#include <linux/uaccess.h>
-#include <linux/bitops.h>
-#include <net/checksum.h>
-
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(memcmp);
-EXPORT_SYMBOL(memchr);
-
-EXPORT_SYMBOL(strncpy);
-EXPORT_SYMBOL(strncmp);
-
-#ifndef CONFIG_GENERIC_CSUM
-EXPORT_SYMBOL(__csum_partial);
-EXPORT_SYMBOL(csum_partial_copy_generic);
-#endif
-
-EXPORT_SYMBOL(__copy_tofrom_user);
-EXPORT_SYMBOL(__clear_user);
-EXPORT_SYMBOL(copy_page);
-
-#ifdef CONFIG_PPC64
-EXPORT_SYMBOL(__arch_hweight8);
-EXPORT_SYMBOL(__arch_hweight16);
-EXPORT_SYMBOL(__arch_hweight32);
-EXPORT_SYMBOL(__arch_hweight64);
-#endif
index beabc68d9a1e4cdb7e40d26045a906173e652790..d13e0760351955b16be34cb5ae2e29484c35c17d 100644 (file)
@@ -11,6 +11,7 @@
 #include <asm/processor.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
        .section __ex_table,"a"
        PPC_LONG_ALIGN
@@ -36,6 +37,7 @@ _GLOBAL(strncpy)
 2:     stbu    r0,1(r6)        /* clear it out if so */
        bdnz    2b
        blr
+EXPORT_SYMBOL(strncpy)
 
 _GLOBAL(strncmp)
        PPC_LCMPI 0,r5,0
@@ -53,6 +55,7 @@ _GLOBAL(strncmp)
        blr
 2:     li      r3,0
        blr
+EXPORT_SYMBOL(strncmp)
 
 #ifdef CONFIG_PPC32
 _GLOBAL(memcmp)
@@ -68,6 +71,7 @@ _GLOBAL(memcmp)
        blr
 2:     li      r3,0
        blr
+EXPORT_SYMBOL(memcmp)
 #endif
 
 _GLOBAL(memchr)
@@ -82,6 +86,7 @@ _GLOBAL(memchr)
        beqlr
 2:     li      r3,0
        blr
+EXPORT_SYMBOL(memchr)
 
 #ifdef CONFIG_PPC32
 _GLOBAL(__clear_user)
@@ -125,4 +130,5 @@ _GLOBAL(__clear_user)
        PPC_LONG        1b,91b
        PPC_LONG        8b,92b
        .text
+EXPORT_SYMBOL(__clear_user)
 #endif
index 7bd9549a90a23f0c82fa5688f1e2ebf8c3845e7f..57ace356c9490fbce556f82c6dd8a74e2fe8655a 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <asm/ppc_asm.h>
 #include <asm/asm-offsets.h>
+#include <asm/export.h>
 
        .section        ".toc","aw"
 PPC64_CACHES:
@@ -200,3 +201,4 @@ err1;       dcbz    r0,r3
        cmpdi   r4,32
        blt     .Lshort_clear
        b       .Lmedium_clear
+EXPORT_SYMBOL(__clear_user)
index 115347f74ce5867199b2b329cc94cbddd0e0d5b4..09cc50c8dace34652b028ab94f8bd8123daffa0e 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/ppc_asm.h>
 #include <asm/thread_info.h>
 #include <asm/asm-offsets.h>
+#include <asm/export.h>
 
 #ifdef CONFIG_SMP
        .section .bss
@@ -33,6 +34,7 @@
        .globl mmu_hash_lock
 mmu_hash_lock:
        .space  4
+EXPORT_SYMBOL(mmu_hash_lock)
 #endif /* CONFIG_SMP */
 
 /*
@@ -575,6 +577,7 @@ _GLOBAL(flush_hash_pages)
        rlwinm  r8,r8,0,31,29           /* clear HASHPTE bit */
        stwcx.  r8,0,r5                 /* update the pte */
        bne-    33b
+EXPORT_SYMBOL(flush_hash_pages)
 
        /* Get the address of the primary PTE group in the hash table (r3) */
 _GLOBAL(flush_hash_patch_A)
index 90480e23fd2c5333bdff811e8e003d32b59e034e..44d3c3a38e3ecc71c779b4a09f07e2c12261a99b 100644 (file)
@@ -529,7 +529,7 @@ static bool might_have_hea(void)
         */
 #ifdef CONFIG_IBMEBUS
        return !cpu_has_feature(CPU_FTR_ARCH_207S) &&
-               !firmware_has_feature(FW_FEATURE_SPLPAR);
+               firmware_has_feature(FW_FEATURE_SPLPAR);
 #else
        return false;
 #endif
index 7c7df40038207b28ca1eaeb745ad725689cc400a..994d1a959e20f4b1cd57112d359b31a1cf31c79b 100644 (file)
@@ -30,8 +30,8 @@ config EP8248E
        select 8272
        select 8260
        select FSL_SOC
-       select PHYLIB
-       select MDIO_BITBANG
+       select PHYLIB if NETDEVICES
+       select MDIO_BITBANG if PHYLIB
        help
          This enables support for the Embedded Planet EP8248E board.
 
index cdab847749e60342b41f4e2bb29e424da843f90d..8fec050f2d5b8a880afb3817ea34349bf49f0aad 100644 (file)
@@ -298,7 +298,9 @@ static const struct of_device_id of_bus_ids[] __initconst = {
 static int __init declare_of_platform_devices(void)
 {
        of_platform_bus_probe(NULL, of_bus_ids, NULL);
-       platform_driver_register(&ep8248e_mdio_driver);
+
+       if (IS_ENABLED(CONFIG_MDIO_BITBANG))
+               platform_driver_register(&ep8248e_mdio_driver);
 
        return 0;
 }
index 17e54339f8d98b1793c29076abca2bbaa0560396..575afd6eb36a405f6a13bf3b7f969d5b6611e7d4 100644 (file)
@@ -30,9 +30,7 @@
  */
 static void __init asp834x_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("asp834x_setup_arch()", 0);
-
+       mpc83xx_setup_arch();
        mpc834x_usb_cfg();
 }
 
index e7fbd6366abbba9a003fa1855e4ee104ef612b56..d8642a4afc743e36183e6602a27b68baf63c221d 100644 (file)
@@ -130,10 +130,7 @@ static void __init mpc83xx_km_setup_arch(void)
        struct device_node *np;
 #endif
 
-       if (ppc_md.progress)
-               ppc_md.progress("kmpbec83xx_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 
 #ifdef CONFIG_QUICC_ENGINE
        np = of_find_node_by_name(NULL, "par_io");
index 8899aa9d11f5fae495d30209706226c1a60461c6..d75c9816a5c92ad4211d71568e90848ab7fc998a 100644 (file)
@@ -142,3 +142,11 @@ void __init mpc83xx_setup_pci(void)
                mpc83xx_add_bridge(np);
 }
 #endif
+
+void __init mpc83xx_setup_arch(void)
+{
+       if (ppc_md.progress)
+               ppc_md.progress("mpc83xx_setup_arch()", 0);
+
+       mpc83xx_setup_pci();
+}
index 040d5d0854675148fc213c7158e887b37c35fb3a..272c41c387b94a00cfe5bc77c541a7ae86a0683a 100644 (file)
  */
 static void __init mpc830x_rdb_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc830x_rdb_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
        mpc831x_usb_cfg();
 }
 
index 40e0d8307b59e3dcca522c1b17e2f0ed1093eff4..fd80fd570e67ba4ca3261c6ee8a3f2e94b625947 100644 (file)
  */
 static void __init mpc831x_rdb_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc831x_rdb_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
        mpc831x_usb_cfg();
 }
 
index cdfa47c4d3941bdc9a4573b3f3fbe821598587a0..bb7b25acf26ffd800d0ead5173c70031b5a488d2 100644 (file)
@@ -58,8 +58,7 @@ static void __init mpc832x_sys_setup_arch(void)
        struct device_node *np;
        u8 __iomem *bcsr_regs = NULL;
 
-       if (ppc_md.progress)
-               ppc_md.progress("mpc832x_sys_setup_arch()", 0);
+       mpc83xx_setup_arch();
 
        /* Map BCSR area */
        np = of_find_node_by_name(NULL, "bcsr");
@@ -71,8 +70,6 @@ static void __init mpc832x_sys_setup_arch(void)
                of_node_put(np);
        }
 
-       mpc83xx_setup_pci();
-
 #ifdef CONFIG_QUICC_ENGINE
        if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) {
                par_io_init(np);
index 0d6a62fc586463bff2da2240925fff9cb3178338..d7c9b186954d931c0957908dbd7a19e3f19d0610 100644 (file)
@@ -197,10 +197,7 @@ static void __init mpc832x_rdb_setup_arch(void)
        struct device_node *np;
 #endif
 
-       if (ppc_md.progress)
-               ppc_md.progress("mpc832x_rdb_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 
 #ifdef CONFIG_QUICC_ENGINE
        if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) {
index 8fd0c1e8b182815fb553aabf00d5fd376c3c22f4..73a5267df497ef267db4c277ea050582de640502 100644 (file)
@@ -57,10 +57,7 @@ machine_device_initcall(mpc834x_itx, mpc834x_itx_declare_of_platform_devices);
  */
 static void __init mpc834x_itx_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc834x_itx_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 
        mpc834x_usb_cfg();
 }
index eeaee6123bb34d48479811ae5759ecc32618adea..009cfc18a4ee3e5c3575fcd410386926ef6cb938 100644 (file)
@@ -76,10 +76,7 @@ static int mpc834xemds_usb_cfg(void)
  */
 static void __init mpc834x_mds_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc834x_mds_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 
        mpc834xemds_usb_cfg();
 }
index dacf4c2df06978d5007462e52dd7c4e826497cf2..4fc3051c2b2eee0bd4c463dec489dcab21d8f099 100644 (file)
@@ -66,8 +66,7 @@ static void __init mpc836x_mds_setup_arch(void)
        struct device_node *np;
        u8 __iomem *bcsr_regs = NULL;
 
-       if (ppc_md.progress)
-               ppc_md.progress("mpc836x_mds_setup_arch()", 0);
+       mpc83xx_setup_arch();
 
        /* Map BCSR area */
        np = of_find_node_by_name(NULL, "bcsr");
@@ -79,8 +78,6 @@ static void __init mpc836x_mds_setup_arch(void)
                of_node_put(np);
        }
 
-       mpc83xx_setup_pci();
-
 #ifdef CONFIG_QUICC_ENGINE
        if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) {
                par_io_init(np);
index cf67ac93ddcb8f341ff27667fac8f9ccf0b3d4e0..93f024fd9b459cc0dcae91af013d7d8f980fb0db 100644 (file)
@@ -31,10 +31,7 @@ machine_device_initcall(mpc836x_rdk, mpc83xx_declare_of_platform_devices);
 
 static void __init mpc836x_rdk_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc836x_rdk_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 }
 
 /*
index 652b97d699c9b7860d2745043af61e78b7f90729..3b34cc1f626c1ef9342e0150cc7e55d906c88961 100644 (file)
@@ -79,10 +79,7 @@ out:
  */
 static void __init mpc837x_mds_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc837x_mds_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
        mpc837xmds_usb_cfg();
 }
 
index 667731d81676a179fe8cf6d65608bbdec0f967cc..0c55fa6af2d5481ed7844bb79ca68998014dc192 100644 (file)
@@ -50,10 +50,7 @@ static void mpc837x_rdb_sd_cfg(void)
  */
 static void __init mpc837x_rdb_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc837x_rdb_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
        mpc837x_usb_cfg();
        mpc837x_rdb_sd_cfg();
 }
index ad484199eff7862881001ba722a738b684d19cd4..636eb9d0401ae354a4e7da56b5d99c01a3890b2c 100644 (file)
@@ -86,5 +86,6 @@ extern void mpc83xx_setup_pci(void);
 #endif
 
 extern int mpc83xx_declare_of_platform_devices(void);
+extern void mpc83xx_setup_arch(void);
 
 #endif                         /* __MPC83XX_H__ */
index b867e88dfb0d292634426d49cb58f039319c95dc..cb4bdabfdf1cd519badabb245b7e0685e515eb09 100644 (file)
  */
 static void __init sbc834x_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("sbc834x_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 }
 
 machine_device_initcall(sbc834x, mpc83xx_declare_of_platform_devices);
index df25a3ed489dd43e578b2f7785116d8ecff1b89b..9dc1d28975b92e4f38c9e89983d7bae444eee495 100644 (file)
@@ -72,7 +72,7 @@ config MPC85xx_CDS
 config MPC85xx_MDS
        bool "Freescale MPC85xx MDS"
        select DEFAULT_UIMAGE
-       select PHYLIB
+       select PHYLIB if NETDEVICES
        select HAS_RAPIDIO
        select SWIOTLB
        help
index 07dd6ae3ec5251b0b0674546246759d0680e1c1a..d2f45569a02647db3abcb35ae4c20778836f6b14 100644 (file)
@@ -72,7 +72,6 @@ define_machine(bsc9132_qds) {
        .pcibios_fixup_bus      = fsl_pcibios_fixup_bus,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index e48f6710e6d5420de6a3802b5712709be92c01d9..0ffdb4a80c2afcf646d1a1d3c1a1fc5545463bf4 100644 (file)
@@ -59,7 +59,6 @@ define_machine(bsc9131_rdb) {
        .setup_arch             = bsc913x_rdb_setup_arch,
        .init_IRQ               = bsc913x_rdb_pic_init,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 3b9e3f0f9aec2f6fa780d70a379e869a4137c7d2..4df1b4026eab4002a588afa5ad8fc887ca3bdc01 100644 (file)
@@ -65,7 +65,6 @@ define_machine(c293_pcie) {
        .setup_arch             = c293_pcie_setup_arch,
        .init_IRQ               = c293_pcie_pic_init,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 3a6a84f07f43c5bbc876ca1b0eeff291044c11d9..1179115a4b5c64aff563af4699e645b311b0e979 100644 (file)
@@ -225,7 +225,6 @@ define_machine(corenet_generic) {
 #else
        .get_irq                = mpic_get_coreint_irq,
 #endif
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 #ifdef CONFIG_PPC64
index 14af36a7fa9caa63ef4bddb0d3544cf4ef26a490..f29c6f0909f354b089b31f3f4773bbc3fa411e10 100644 (file)
@@ -215,7 +215,6 @@ define_machine(ge_imp3a) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 6ba687f19e45dc86dd1911a90d519546244acfad..94a7f92c858ffee2c41903f16f82a5cd492522fe 100644 (file)
@@ -77,7 +77,6 @@ define_machine(mpc8536_ds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 8756715c7a47fa12152bc53cd69077f43f835680..f3e055fdd1de436450d8035ba6800b950eaa62e9 100644 (file)
@@ -170,7 +170,6 @@ define_machine(mpc85xx_ads) {
        .init_IRQ               = mpc85xx_ads_pic_init,
        .show_cpuinfo           = mpc85xx_ads_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 86f20156178e919b5972ce4feeb22080a90a2bf9..224db30c497b0665284aafafa6e9edeec4e85ec3 100644 (file)
@@ -83,7 +83,8 @@ static int mpc85xx_exclude_device(struct pci_controller *hose,
                return PCIBIOS_SUCCESSFUL;
 }
 
-static void __noreturn mpc85xx_cds_restart(char *cmd)
+static int mpc85xx_cds_restart(struct notifier_block *this,
+                              unsigned long mode, void *cmd)
 {
        struct pci_dev *dev;
        u_char tmp;
@@ -108,12 +109,25 @@ static void __noreturn mpc85xx_cds_restart(char *cmd)
        }
 
        /*
-        *  If we can't find the VIA chip (maybe the P2P bridge is disabled)
-        *  or the VIA chip reset didn't work, just use the default reset.
+        *  If we can't find the VIA chip (maybe the P2P bridge is
+        *  disabled) or the VIA chip reset didn't work, just return
+        *  and let default reset sequence happen.
         */
-       fsl_rstcr_restart(NULL);
+       return NOTIFY_DONE;
 }
 
+static int mpc85xx_cds_restart_register(void)
+{
+       static struct notifier_block restart_handler;
+
+       restart_handler.notifier_call = mpc85xx_cds_restart;
+       restart_handler.priority = 192;
+
+       return register_restart_handler(&restart_handler);
+}
+machine_arch_initcall(mpc85xx_cds, mpc85xx_cds_restart_register);
+
+
 static void __init mpc85xx_cds_pci_irq_fixup(struct pci_dev *dev)
 {
        u_char c;
@@ -380,11 +394,8 @@ define_machine(mpc85xx_cds) {
        .show_cpuinfo   = mpc85xx_cds_show_cpuinfo,
        .get_irq        = mpic_get_irq,
 #ifdef CONFIG_PCI
-       .restart        = mpc85xx_cds_restart,
        .pcibios_fixup_bus      = mpc85xx_cds_fixup_bus,
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
-#else
-       .restart        = fsl_rstcr_restart,
 #endif
        .calibrate_decr = generic_calibrate_decr,
        .progress       = udbg_progress,
index ed69c7ee1829b94d1c060aed38eef408bf7baf3b..dc9e035cc637a749061a2b25e1a44d93998f8289 100644 (file)
@@ -204,7 +204,6 @@ define_machine(mpc8544_ds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -219,7 +218,6 @@ define_machine(mpc8572_ds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -234,7 +232,6 @@ define_machine(p2020_ds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index fa9cd710d2aee15fd33ce501445fd5c84ede0b5f..d7e440e6dba3d3d45615e26f26894bf09bb1615c 100644 (file)
@@ -63,6 +63,8 @@
 #define DBG(fmt...)
 #endif
 
+#if IS_BUILTIN(CONFIG_PHYLIB)
+
 #define MV88E1111_SCR  0x10
 #define MV88E1111_SCR_125CLK   0x0010
 static int mpc8568_fixup_125_clock(struct phy_device *phydev)
@@ -152,6 +154,8 @@ static int mpc8568_mds_phy_fixups(struct phy_device *phydev)
        return err;
 }
 
+#endif
+
 /* ************************************************************************
  *
  * Setup the architecture
@@ -313,6 +317,7 @@ static void __init mpc85xx_mds_setup_arch(void)
        swiotlb_detect_4g();
 }
 
+#if IS_BUILTIN(CONFIG_PHYLIB)
 
 static int __init board_fixups(void)
 {
@@ -342,9 +347,12 @@ static int __init board_fixups(void)
 
        return 0;
 }
+
 machine_arch_initcall(mpc8568_mds, board_fixups);
 machine_arch_initcall(mpc8569_mds, board_fixups);
 
+#endif
+
 static int __init mpc85xx_publish_devices(void)
 {
        if (machine_is(mpc8568_mds))
@@ -385,7 +393,6 @@ define_machine(mpc8568_mds) {
        .setup_arch     = mpc85xx_mds_setup_arch,
        .init_IRQ       = mpc85xx_mds_pic_init,
        .get_irq        = mpic_get_irq,
-       .restart        = fsl_rstcr_restart,
        .calibrate_decr = generic_calibrate_decr,
        .progress       = udbg_progress,
 #ifdef CONFIG_PCI
@@ -405,7 +412,6 @@ define_machine(mpc8569_mds) {
        .setup_arch     = mpc85xx_mds_setup_arch,
        .init_IRQ       = mpc85xx_mds_pic_init,
        .get_irq        = mpic_get_irq,
-       .restart        = fsl_rstcr_restart,
        .calibrate_decr = generic_calibrate_decr,
        .progress       = udbg_progress,
 #ifdef CONFIG_PCI
@@ -426,7 +432,6 @@ define_machine(p1021_mds) {
        .setup_arch     = mpc85xx_mds_setup_arch,
        .init_IRQ       = mpc85xx_mds_pic_init,
        .get_irq        = mpic_get_irq,
-       .restart        = fsl_rstcr_restart,
        .calibrate_decr = generic_calibrate_decr,
        .progress       = udbg_progress,
 #ifdef CONFIG_PCI
@@ -434,4 +439,3 @@ define_machine(p1021_mds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
 };
-
index c1499cbf3786746be655ac13787f6034b1cd086c..10069503e39f2fab8ce33c4a2b27d2e5041ed32b 100644 (file)
@@ -213,7 +213,6 @@ define_machine(p2020_rdb) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -228,7 +227,6 @@ define_machine(p1020_rdb) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -243,7 +241,6 @@ define_machine(p1021_rdb_pc) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -258,7 +255,6 @@ define_machine(p2020_rdb_pc) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -273,7 +269,6 @@ define_machine(p1025_rdb) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -288,7 +283,6 @@ define_machine(p1020_mbg_pc) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -303,7 +297,6 @@ define_machine(p1020_utm_pc) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -318,7 +311,6 @@ define_machine(p1020_rdb_pc) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -333,7 +325,6 @@ define_machine(p1020_rdb_pd) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -348,7 +339,6 @@ define_machine(p1024_rdb) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index acc3d0d6049d50c650a46002bd9859a374ce3326..d5af0723a69e97f58e6fba26e3694469440b2bc2 100644 (file)
@@ -66,7 +66,6 @@ define_machine(mvme2500) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 661d7b59e4131d8bbe332b0b7f555d4252a7d237..78d13b364cd631e6c4b2863171fc8382796c4490 100644 (file)
@@ -79,7 +79,6 @@ define_machine(p1010_rdb) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 63568d68c76f8c50554f4126a8672ea20118603f..0908abd7e36f684cf60162fd572a6175abfeb833 100644 (file)
@@ -568,7 +568,6 @@ define_machine(p1022_ds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 2f29436003011ecf24612275b22dfc8f234d5b71..276e00ab3dde97a766b6652d7f9dd714a6ea302f 100644 (file)
@@ -148,7 +148,6 @@ define_machine(p1022_rdk) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 40d8de57c341dc73924130ad23d9a4a666069e1b..3e8cd0324dfcab91585beeb8962a863d53cab885 100644 (file)
@@ -110,7 +110,6 @@ define_machine(p1023_rdb) {
        .setup_arch             = mpc85xx_rdb_setup_arch,
        .init_IRQ               = mpc85xx_rdb_pic_init,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 #ifdef CONFIG_PCI
index 2410167b290a256232a82f37c5c04eb3d300d6eb..33c5ba644fa5683afa1f09839ae8fa23105a0285 100644 (file)
@@ -91,7 +91,6 @@ define_machine(ppa8548) {
        .init_IRQ       = ppa8548_pic_init,
        .show_cpuinfo   = ppa8548_show_cpuinfo,
        .get_irq        = mpic_get_irq,
-       .restart        = fsl_rstcr_restart,
        .calibrate_decr = generic_calibrate_decr,
        .progress       = udbg_progress,
 };
index 50d745809809a693daa11e3079a8f060e06962e1..b63a8548366f8d6e3218624784e7875edad01ee5 100644 (file)
@@ -77,7 +77,6 @@ define_machine(qemu_e500) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_coreint_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 62b6c45a5a9b162db5b1c8e3c7ded9d0b1ccc78d..2c670848ff0804b5110a092e1429401cd1574be2 100644 (file)
@@ -130,7 +130,6 @@ define_machine(sbc8548) {
        .init_IRQ       = sbc8548_pic_init,
        .show_cpuinfo   = sbc8548_show_cpuinfo,
        .get_irq        = mpic_get_irq,
-       .restart        = fsl_rstcr_restart,
 #ifdef CONFIG_PCI
        .pcibios_fixup_bus      = fsl_pcibios_fixup_bus,
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
index 79fd0dfd4b8203f95a07d6bef19407d309708772..21d6aaa5c3e4aedced415e9409591049a63cc7cc 100644 (file)
@@ -38,18 +38,18 @@ static void gpio_halt_wfn(struct work_struct *work)
 }
 static DECLARE_WORK(gpio_halt_wq, gpio_halt_wfn);
 
-static void gpio_halt_cb(void)
+static void __noreturn gpio_halt_cb(void)
 {
        enum of_gpio_flags flags;
        int trigger, gpio;
 
        if (!halt_node)
-               return;
+               panic("No reset GPIO information was provided in DT\n");
 
        gpio = of_get_gpio_flags(halt_node, 0, &flags);
 
        if (!gpio_is_valid(gpio))
-               return;
+               panic("Provided GPIO is invalid\n");
 
        trigger = (flags == OF_GPIO_ACTIVE_LOW);
 
@@ -57,6 +57,8 @@ static void gpio_halt_cb(void)
 
        /* Probably wont return */
        gpio_set_value(gpio, trigger);
+
+       panic("Halt failed\n");
 }
 
 /* This IRQ means someone pressed the power button and it is waiting for us
index cd255acde2e29ec7e9f60705bb5069ca3d4f1816..8da4ed90338d56090fac9cb5bc371879d3a05f67 100644 (file)
@@ -91,7 +91,6 @@ define_machine(socrates) {
        .setup_arch             = socrates_setup_arch,
        .init_IRQ               = socrates_pic_init,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 91b824c4dc082aea8add1e371c1553dcac3d5c8e..1a1d44ea17541b1bbca35562f2a936594d73af71 100644 (file)
@@ -103,7 +103,6 @@ define_machine(stx_gp3) {
        .init_IRQ               = stx_gp3_pic_init,
        .show_cpuinfo           = stx_gp3_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index b7c54454d611a9db3b6ee1cbe882157bf25d5d6c..9fc20a37835e62b5247bf5d37cf5283e06825f7c 100644 (file)
@@ -132,7 +132,6 @@ define_machine(tqm85xx) {
        .init_IRQ               = tqm85xx_pic_init,
        .show_cpuinfo           = tqm85xx_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 1bc02a87f5971ddaa844b1822332e3590f48fda7..360f6253e9ffca80f009e363d47e032cf715a2ca 100644 (file)
@@ -140,7 +140,6 @@ define_machine(twr_p1025) {
        .pcibios_fixup_bus      = fsl_pcibios_fixup_bus,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index cf0c70ff026e7aa2c65948e3600b6c155ecd5819..cd6ce845f398b69334dfa8602bd7ec37c7ad44e8 100644 (file)
@@ -167,7 +167,6 @@ define_machine(xes_mpc8572) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -182,7 +181,6 @@ define_machine(xes_mpc8548) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -197,7 +195,6 @@ define_machine(xes_mpc8540) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index ef684afb63c62433eaf1caec591c772592c2a640..6b99300edd36585cba83d65feeccdfb4076f6297 100644 (file)
@@ -204,7 +204,6 @@ define_machine(gef_ppc9a) {
        .init_IRQ               = gef_ppc9a_init_irq,
        .show_cpuinfo           = gef_ppc9a_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index 67dd0c23164604c732a36a31f0ac7d254441d2e8..8cdeca0611279b8608c4da431782a8269d36a4dc 100644 (file)
@@ -191,7 +191,6 @@ define_machine(gef_sbc310) {
        .init_IRQ               = gef_sbc310_init_irq,
        .show_cpuinfo           = gef_sbc310_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index 805026976cacd10fc4a22b381e691b6756dd5596..da8723ae23ecfe1a51d95bc34dcd4fea0a832519 100644 (file)
@@ -181,7 +181,6 @@ define_machine(gef_sbc610) {
        .init_IRQ               = gef_sbc610_init_irq,
        .show_cpuinfo           = gef_sbc610_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index fef0582eddf1572fd54ea80886db22421d83bf1f..a5d73fabe4d11dfa5746c9ee9f6072808f45df54 100644 (file)
@@ -331,7 +331,6 @@ define_machine(mpc86xx_hpcd) {
        .setup_arch             = mpc86xx_hpcd_setup_arch,
        .init_IRQ               = mpc86xx_init_irq,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index 5ae42a037065f6d6246def005d3aa021004c7b0f..a0e989ed4b6f425036e880d886b6a15476bf9e85 100644 (file)
@@ -130,7 +130,6 @@ define_machine(mpc86xx_hpcn) {
        .init_IRQ               = mpc86xx_init_irq,
        .show_cpuinfo           = mpc86xx_hpcn_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index addb41e7cd144c9909b655073963cf759007f6ad..835352e63dc3a62213ffb22e40b48105fdb6c467 100644 (file)
@@ -111,7 +111,6 @@ define_machine(mvme7100) {
        .setup_arch             = mvme7100_setup_arch,
        .init_IRQ               = mpc86xx_init_irq,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index 52af5735742e4f8784e1cfc7e944489d6c96f33b..93db35d4f6eb902c3a9efccc0354827bf2596e78 100644 (file)
@@ -82,7 +82,6 @@ define_machine(sbc8641) {
        .init_IRQ               = mpc86xx_init_irq,
        .show_cpuinfo           = sbc8641_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index 86707e67843f6a152e8339ca7d600c27211fefac..aa35245d8d6d337204806bbb5888e9df4d5455ef 100644 (file)
@@ -393,7 +393,7 @@ static void __pSeries_lpar_hugepage_invalidate(unsigned long *slot,
                                             unsigned long *vpn, int count,
                                             int psize, int ssize)
 {
-       unsigned long param[8];
+       unsigned long param[PLPAR_HCALL9_BUFSIZE];
        int i = 0, pix = 0, rc;
        unsigned long flags = 0;
        int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
@@ -522,7 +522,7 @@ static void pSeries_lpar_flush_hash_range(unsigned long number, int local)
        unsigned long flags = 0;
        struct ppc64_tlb_batch *batch = this_cpu_ptr(&ppc64_tlb_batch);
        int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
-       unsigned long param[9];
+       unsigned long param[PLPAR_HCALL9_BUFSIZE];
        unsigned long hash, index, shift, hidx, slot;
        real_pte_t pte;
        int psize, ssize;
index 2e4ebd0e25b3e84431335c8d2be07d9485af5078..ec2d5c835170a6753a588d4ba6a16d2c51412d3c 100755 (executable)
@@ -30,6 +30,7 @@ bad_relocs=$(
        # On PPC64:
        #       R_PPC64_RELATIVE, R_PPC64_NONE
        #       R_PPC64_ADDR64 mach_<name>
+       #       R_PPC64_ADDR64 __crc_<name>
        # On PPC:
        #       R_PPC_RELATIVE, R_PPC_ADDR16_HI,
        #       R_PPC_ADDR16_HA,R_PPC_ADDR16_LO,
@@ -41,7 +42,8 @@ R_PPC_ADDR16_HI
 R_PPC_ADDR16_HA
 R_PPC_RELATIVE
 R_PPC_NONE' |
-       grep -E -v '\<R_PPC64_ADDR64[[:space:]]+mach_'
+       grep -E -v '\<R_PPC64_ADDR64[[:space:]]+mach_' |
+       grep -E -v '\<R_PPC64_ADDR64[[:space:]]+__crc_'
 )
 
 if [ -z "$bad_relocs" ]; then
index 3c0eb9b255353acf5bac69bf25ef66bee44dafdc..986cd111d4df1064161238a6b2fabad474fa5260 100644 (file)
@@ -233,8 +233,6 @@ void __init cpm_reset(void)
        else
                out_be32(&siu_conf->sc_sdcr, 1);
        immr_unmap(siu_conf);
-
-       cpm_muram_init();
 }
 
 static DEFINE_SPINLOCK(cmd_lock);
index 8dc1e24f3c2383bbaf22f2486b9ba076220413ff..f78ff841652c2fd2006b977db12e28447661aa87 100644 (file)
@@ -66,10 +66,6 @@ void __init cpm2_reset(void)
        cpm2_immr = ioremap(get_immrbase(), CPM_MAP_SIZE);
 #endif
 
-       /* Reclaim the DP memory for our use.
-        */
-       cpm_muram_init();
-
        /* Tell everyone where the comm processor resides.
         */
        cpmp = &cpm2_immr->im_cpm;
index 947f42007734c1c22d5f6eb02ae72f110cd5f44b..51bf749a4f3a7b6c8a245c8d793cee0cc47f9a51 100644 (file)
 #include <linux/of_gpio.h>
 #endif
 
+static int __init cpm_init(void)
+{
+       struct device_node *np;
+
+       np = of_find_compatible_node(NULL, NULL, "fsl,cpm1");
+       if (!np)
+               np = of_find_compatible_node(NULL, NULL, "fsl,cpm2");
+       if (!np)
+               return -ENODEV;
+       cpm_muram_init();
+       of_node_put(np);
+       return 0;
+}
+subsys_initcall(cpm_init);
+
 #ifdef CONFIG_PPC_EARLY_DEBUG_CPM
 static u32 __iomem *cpm_udbg_txdesc;
 static u8 __iomem *cpm_udbg_txbuf;
index d3098ef1404a2df72738bd1e01e51dda3f185227..e687bb2003ff0af93028c14de2c75ad9d37b2136 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/ppc_asm.h>
 #include <asm/processor.h>
 #include <asm/bug.h>
+#include <asm/export.h>
 
 #define DCR_ACCESS_PROLOG(table) \
        cmpli   cr0,r3,1024;     \
 
 _GLOBAL(__mfdcr)
        DCR_ACCESS_PROLOG(__mfdcr_table)
+EXPORT_SYMBOL(__mfdcr)
 
 _GLOBAL(__mtdcr)
        DCR_ACCESS_PROLOG(__mtdcr_table)
+EXPORT_SYMBOL(__mtdcr)
 
 __mfdcr_table:
        mfdcr  r3,0; blr
index 0ef9df49f0f2c2aca631963a188057d64ffe4098..d3a597456b6e57f83efb165e065a572d0105cc99 100644 (file)
@@ -111,8 +111,7 @@ static struct pci_ops fsl_indirect_pcie_ops =
        .write = indirect_write_config,
 };
 
-#define MAX_PHYS_ADDR_BITS     40
-static u64 pci64_dma_offset = 1ull << MAX_PHYS_ADDR_BITS;
+static u64 pci64_dma_offset;
 
 #ifdef CONFIG_SWIOTLB
 static void setup_swiotlb_ops(struct pci_controller *hose)
@@ -132,12 +131,10 @@ static int fsl_pci_dma_set_mask(struct device *dev, u64 dma_mask)
                return -EIO;
 
        /*
-        * Fixup PCI devices that are able to DMA to above the physical
-        * address width of the SoC such that we can address any internal
-        * SoC address from across PCI if needed
+        * Fix up PCI devices that are able to DMA to the large inbound
+        * mapping that allows addressing any RAM address from across PCI.
         */
-       if ((dev_is_pci(dev)) &&
-           dma_mask >= DMA_BIT_MASK(MAX_PHYS_ADDR_BITS)) {
+       if (dev_is_pci(dev) && dma_mask >= pci64_dma_offset * 2 - 1) {
                set_dma_ops(dev, &dma_direct_ops);
                set_dma_offset(dev, pci64_dma_offset);
        }
@@ -387,6 +384,7 @@ static void setup_pci_atmu(struct pci_controller *hose)
                                mem_log++;
 
                        piwar = (piwar & ~PIWAR_SZ_MASK) | (mem_log - 1);
+                       pci64_dma_offset = 1ULL << mem_log;
 
                        if (setup_inbound) {
                                /* Setup inbound memory window */
index a09ca704de58af05ce66c8229b9ddfca2bf834b1..d93056eedcb0a4a3c01e51c2ff3dc0e9b6163c77 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/fsl_devices.h>
 #include <linux/fs_enet_pd.h>
 #include <linux/fs_uart_pd.h>
+#include <linux/reboot.h>
 
 #include <linux/atomic.h>
 #include <asm/io.h>
@@ -180,23 +181,38 @@ EXPORT_SYMBOL(get_baudrate);
 #if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
 static __be32 __iomem *rstcr;
 
+static int fsl_rstcr_restart(struct notifier_block *this,
+                            unsigned long mode, void *cmd)
+{
+       local_irq_disable();
+       /* set reset control register */
+       out_be32(rstcr, 0x2);   /* HRESET_REQ */
+
+       return NOTIFY_DONE;
+}
+
 static int __init setup_rstcr(void)
 {
        struct device_node *np;
 
+       static struct notifier_block restart_handler = {
+               .notifier_call = fsl_rstcr_restart,
+               .priority = 128,
+       };
+
        for_each_node_by_name(np, "global-utilities") {
                if ((of_get_property(np, "fsl,has-rstcr", NULL))) {
                        rstcr = of_iomap(np, 0) + 0xb0;
-                       if (!rstcr)
+                       if (!rstcr) {
                                printk (KERN_ERR "Error: reset control "
                                                "register not mapped!\n");
+                       } else {
+                               register_restart_handler(&restart_handler);
+                       }
                        break;
                }
        }
 
-       if (!rstcr && ppc_md.restart == fsl_rstcr_restart)
-               printk(KERN_ERR "No RSTCR register, warm reboot won't work\n");
-
        of_node_put(np);
 
        return 0;
@@ -204,15 +220,6 @@ static int __init setup_rstcr(void)
 
 arch_initcall(setup_rstcr);
 
-void __noreturn fsl_rstcr_restart(char *cmd)
-{
-       local_irq_disable();
-       if (rstcr)
-               /* set reset control register */
-               out_be32(rstcr, 0x2);   /* HRESET_REQ */
-
-       while (1) ;
-}
 #endif
 
 #if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
index 433566a5ef192703d74c6ac64728c1441d6aa22e..d73daa4f0ccfc9005ff2cca7df398a590f709179 100644 (file)
@@ -19,8 +19,6 @@ extern u32 fsl_get_sys_freq(void);
 struct spi_board_info;
 struct device_node;
 
-extern void __noreturn fsl_rstcr_restart(char *cmd);
-
 /* The different ports that the DIU can be connected to */
 enum fsl_diu_monitor_port {
        FSL_DIU_PORT_DVI,       /* DVI */
index 4d48cecfedd1d6b7dee121db11fdf35b295bec23..b9aac951a90f69ec3e05775fe50495b25fea6716 100644 (file)
@@ -1249,7 +1249,7 @@ struct mpic * __init mpic_alloc(struct device_node *node,
        /* Pick the physical address from the device tree if unspecified */
        if (!phys_addr) {
                /* Check if it is DCR-based */
-               if (of_get_property(node, "dcr-reg", NULL)) {
+               if (of_property_read_bool(node, "dcr-reg")) {
                        flags |= MPIC_USES_DCR;
                } else {
                        struct resource r;
index 9043d2e1e2ae0b3c01a7b6588bed848f44dd92ff..20f196b82a6e65ad9f792d5d885df27441aeddaa 100644 (file)
@@ -1,6 +1,7 @@
 
 
 generic-y += clkdev.h
+generic-y += export.h
 generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += mm-arch-hooks.h
index 72ccc41444dc64a1ddef531fd635ddf24bbbc6be..1f0fe98f6db927cea10038e5b0adbfaa57dbab93 100644 (file)
@@ -61,7 +61,7 @@ obj-y += entry.o reipl.o relocate_kernel.o
 
 extra-y                                += head.o head64.o vmlinux.lds
 
-obj-$(CONFIG_MODULES)          += s390_ksyms.o module.o
+obj-$(CONFIG_MODULES)          += module.o
 obj-$(CONFIG_SMP)              += smp.o
 obj-$(CONFIG_SCHED_TOPOLOGY)   += topology.o
 obj-$(CONFIG_HIBERNATION)      += suspend.o swsusp.o
index c51650a1ed167d533922f1b205c666368d5cc9ad..49a30737addef4103184ad92907b6f1f5b911bfa 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/vx-insn.h>
 #include <asm/setup.h>
 #include <asm/nmi.h>
+#include <asm/export.h>
 
 __PT_R0      = __PT_GPRS
 __PT_R1      = __PT_GPRS + 8
@@ -259,6 +260,8 @@ sie_exit:
 
        EX_TABLE(.Lrewind_pad,.Lsie_fault)
        EX_TABLE(sie_exit,.Lsie_fault)
+EXPORT_SYMBOL(sie64a)
+EXPORT_SYMBOL(sie_exit)
 #endif
 
 /*
@@ -825,6 +828,9 @@ ENTRY(save_fpu_regs)
        oi      __LC_CPU_FLAGS+7,_CIF_FPU
        br      %r14
 .Lsave_fpu_regs_end:
+#if IS_ENABLED(CONFIG_KVM)
+EXPORT_SYMBOL(save_fpu_regs)
+#endif
 
 /*
  * Load floating-point controls and floating-point or vector registers.
index e499370fbccb328561e6ef5ab0bfe35afb4a0b51..9a17e4475d2779d088fc3fb1deabf250dd09a94f 100644 (file)
@@ -9,6 +9,7 @@
 #include <asm/asm-offsets.h>
 #include <asm/ftrace.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
        .section .kprobes.text, "ax"
 
@@ -23,6 +24,8 @@ ENTRY(ftrace_stub)
 ENTRY(_mcount)
        br      %r14
 
+EXPORT_SYMBOL(_mcount)
+
 ENTRY(ftrace_caller)
        .globl  ftrace_regs_caller
        .set    ftrace_regs_caller,ftrace_caller
diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c
deleted file mode 100644 (file)
index e67453b..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <linux/module.h>
-#include <linux/kvm_host.h>
-#include <asm/fpu/api.h>
-#include <asm/ftrace.h>
-
-#ifdef CONFIG_FUNCTION_TRACER
-EXPORT_SYMBOL(_mcount);
-#endif
-#if IS_ENABLED(CONFIG_KVM)
-EXPORT_SYMBOL(sie64a);
-EXPORT_SYMBOL(sie_exit);
-EXPORT_SYMBOL(save_fpu_regs);
-#endif
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
index c6d553e85ab1112e3a6dffd2a0c0f0aa870b6620..be9fa65bfac4e16d1d71f5dc6ee3dcb7c9d69299 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 /*
  * memset implementation
@@ -60,6 +61,7 @@ ENTRY(memset)
        xc      0(1,%r1),0(%r1)
 .Lmemset_mvc:
        mvc     1(1,%r1),0(%r1)
+EXPORT_SYMBOL(memset)
 
 /*
  * memcpy implementation
@@ -86,3 +88,4 @@ ENTRY(memcpy)
        j       .Lmemcpy_rest
 .Lmemcpy_mvc:
        mvc     0(1,%r1),0(%r3)
+EXPORT_SYMBOL(memcpy)
index adb0c34bf431e121d66caff904c30fb7e63e933d..18d4107e10eefb5e2ea5935412a95e7a03f2ae41 100644 (file)
@@ -266,7 +266,8 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
        /* Try to get the remaining pages with get_user_pages */
        start += nr << PAGE_SHIFT;
        pages += nr;
-       ret = get_user_pages_unlocked(start, nr_pages - nr, write, 0, pages);
+       ret = get_user_pages_unlocked(start, nr_pages - nr, pages,
+                                     write ? FOLL_WRITE : 0);
        /* Have to be a bit careful with return values */
        if (nr > 0)
                ret = (ret < 0) ? nr : ret + nr;
index 55836188b217c170c3141558d843e1d4259bf165..4f7314d5f3347d40a6282976d538ccfb0e4d447d 100644 (file)
@@ -131,7 +131,7 @@ read_tsk_long(struct task_struct *child,
 {
        int copied;
 
-       copied = access_process_vm(child, addr, res, sizeof(*res), 0);
+       copied = access_process_vm(child, addr, res, sizeof(*res), FOLL_FORCE);
 
        return copied != sizeof(*res) ? -EIO : 0;
 }
@@ -142,7 +142,7 @@ read_tsk_short(struct task_struct *child,
 {
        int copied;
 
-       copied = access_process_vm(child, addr, res, sizeof(*res), 0);
+       copied = access_process_vm(child, addr, res, sizeof(*res), FOLL_FORCE);
 
        return copied != sizeof(*res) ? -EIO : 0;
 }
@@ -153,7 +153,8 @@ write_tsk_short(struct task_struct *child,
 {
        int copied;
 
-       copied = access_process_vm(child, addr, &val, sizeof(val), 1);
+       copied = access_process_vm(child, addr, &val, sizeof(val),
+                       FOLL_FORCE | FOLL_WRITE);
 
        return copied != sizeof(val) ? -EIO : 0;
 }
@@ -164,7 +165,8 @@ write_tsk_long(struct task_struct *child,
 {
        int copied;
 
-       copied = access_process_vm(child, addr, &val, sizeof(val), 1);
+       copied = access_process_vm(child, addr, &val, sizeof(val),
+                       FOLL_FORCE | FOLL_WRITE);
 
        return copied != sizeof(val) ? -EIO : 0;
 }
index 1517a7dcd6d92a3e49ec9f847456de3156267bfe..5cea1e750ceca0b7ad0b7baac431218e695dafb8 100644 (file)
@@ -29,6 +29,7 @@
 #include <asm/cacheflush.h>
 #include <asm/irq.h>
 #include <asm/irq_regs.h>
+#include <asm/uaccess.h>
 
 unsigned long exception_handlers[32];
 
index 00476662ac2c07ba1e0a3bb427e114ae2f5a7650..336f33a419d99561d57b329a3ee6a513164ca43f 100644 (file)
@@ -31,7 +31,7 @@ isa-y                                 := $(isa-y)-up
 endif
 
 cflags-$(CONFIG_CPU_SH2)               := $(call cc-option,-m2,)
-cflags-$(CONFIG_CPU_J2)                        := $(call cc-option,-mj2,)
+cflags-$(CONFIG_CPU_J2)                        += $(call cc-option,-mj2,)
 cflags-$(CONFIG_CPU_SH2A)              += $(call cc-option,-m2a,) \
                                           $(call cc-option,-m2a-nofpu,) \
                                           $(call cc-option,-m4-nofpu,)
index e9c2c42031fee497379af3e3fff29d519275eaee..4e21949593cf59f20c4290285d7c4c28db7acc30 100644 (file)
@@ -22,6 +22,16 @@ config SH_DEVICE_TREE
          have sufficient driver coverage to use this option; do not
          select it if you are using original SuperH hardware.
 
+config SH_JCORE_SOC
+       bool "J-Core SoC"
+       depends on SH_DEVICE_TREE && (CPU_SH2 || CPU_J2)
+       select CLKSRC_JCORE_PIT
+       select JCORE_AIC
+       default y if CPU_J2
+       help
+         Select this option to include drivers core components of the
+         J-Core SoC, including interrupt controllers and timers.
+
 config SH_SOLUTION_ENGINE
        bool "SolutionEngine"
        select SOLUTION_ENGINE
index 94d1eca52f723e82df0792c96d87783c6a422245..2eb81ebe3888bf8a4bdd2af4b575c0e254f52f02 100644 (file)
@@ -8,6 +8,7 @@ CONFIG_MEMORY_START=0x10000000
 CONFIG_MEMORY_SIZE=0x04000000
 CONFIG_CPU_BIG_ENDIAN=y
 CONFIG_SH_DEVICE_TREE=y
+CONFIG_SH_JCORE_SOC=y
 CONFIG_HZ_100=y
 CONFIG_CMDLINE_OVERWRITE=y
 CONFIG_CMDLINE="console=ttyUL0 earlycon"
@@ -20,6 +21,7 @@ CONFIG_INET=y
 CONFIG_DEVTMPFS=y
 CONFIG_DEVTMPFS_MOUNT=y
 CONFIG_NETDEVICES=y
+CONFIG_SERIAL_EARLYCON=y
 CONFIG_SERIAL_UARTLITE=y
 CONFIG_SERIAL_UARTLITE_CONSOLE=y
 CONFIG_I2C=y
index 40fa6c8adc43a361c60ffee8929aa39d2449e926..063c298ba56cc900479eb9b50a8c9587678f6893 100644 (file)
@@ -258,7 +258,8 @@ slow_irqon:
                pages += nr;
 
                ret = get_user_pages_unlocked(start,
-                       (end - start) >> PAGE_SHIFT, write, 0, pages);
+                       (end - start) >> PAGE_SHIFT, pages,
+                       write ? FOLL_WRITE : 0);
 
                /* Have to be a bit careful with return values */
                if (nr > 0) {
index 6024c26c058565c76f20ebe98a3c3b0ad5a96120..cfc918067f80aa01f9d976819608afe3f6729a25 100644 (file)
@@ -6,6 +6,7 @@ generic-y += cputime.h
 generic-y += div64.h
 generic-y += emergency-restart.h
 generic-y += exec.h
+generic-y += export.h
 generic-y += irq_regs.h
 generic-y += irq_work.h
 generic-y += linkage.h
index 98b72a0c8e6e624d0ea6618e4f8ace5736369309..86f34be14ce0065671587ea5ca9558c5d3b8096d 100644 (file)
@@ -5,4 +5,38 @@
 #else
 #include <asm/string_32.h>
 #endif
+
+/* First the mem*() things. */
+#define __HAVE_ARCH_MEMMOVE
+void *memmove(void *, const void *, __kernel_size_t);
+
+#define __HAVE_ARCH_MEMCPY
+#define memcpy(t, f, n) __builtin_memcpy(t, f, n)
+
+#define __HAVE_ARCH_MEMSET
+#define memset(s, c, count) __builtin_memset(s, c, count)
+
+#define __HAVE_ARCH_MEMSCAN
+
+#define memscan(__arg0, __char, __arg2)                                                \
+({                                                                             \
+       void *__memscan_zero(void *, size_t);                                   \
+       void *__memscan_generic(void *, int, size_t);                           \
+       void *__retval, *__addr = (__arg0);                                     \
+       size_t __size = (__arg2);                                               \
+                                                                               \
+       if(__builtin_constant_p(__char) && !(__char))                           \
+               __retval = __memscan_zero(__addr, __size);                      \
+       else                                                                    \
+               __retval = __memscan_generic(__addr, (__char), __size);         \
+                                                                               \
+       __retval;                                                               \
+})
+
+#define __HAVE_ARCH_MEMCMP
+int memcmp(const void *,const void *,__kernel_size_t);
+
+#define __HAVE_ARCH_STRNCMP
+int strncmp(const char *, const char *, __kernel_size_t);
+
 #endif
index 69974e924611191ae448d8707affaec8ac4c3de9..649412476a6949b69d57e753fcb766ceb35f9960 100644 (file)
 
 #include <asm/page.h>
 
-/* Really, userland/ksyms should not see any of this stuff. */
-
-#ifdef __KERNEL__
-
-void __memmove(void *,const void *,__kernel_size_t);
-
-#ifndef EXPORT_SYMTAB_STROPS
-
-/* First the mem*() things. */
-#define __HAVE_ARCH_MEMMOVE
-#undef memmove
-#define memmove(_to, _from, _n) \
-({ \
-       void *_t = (_to); \
-       __memmove(_t, (_from), (_n)); \
-       _t; \
-})
-
-#define __HAVE_ARCH_MEMCPY
-#define memcpy(t, f, n) __builtin_memcpy(t, f, n)
-
-#define __HAVE_ARCH_MEMSET
-#define memset(s, c, count) __builtin_memset(s, c, count)
-
-#define __HAVE_ARCH_MEMSCAN
-
-#undef memscan
-#define memscan(__arg0, __char, __arg2)                                                \
-({                                                                             \
-       void *__memscan_zero(void *, size_t);                                   \
-       void *__memscan_generic(void *, int, size_t);                           \
-       void *__retval, *__addr = (__arg0);                                     \
-       size_t __size = (__arg2);                                               \
-                                                                               \
-       if(__builtin_constant_p(__char) && !(__char))                           \
-               __retval = __memscan_zero(__addr, __size);                      \
-       else                                                                    \
-               __retval = __memscan_generic(__addr, (__char), __size);         \
-                                                                               \
-       __retval;                                                               \
-})
-
-#define __HAVE_ARCH_MEMCMP
-int memcmp(const void *,const void *,__kernel_size_t);
-
-/* Now the str*() stuff... */
-#define __HAVE_ARCH_STRLEN
-__kernel_size_t strlen(const char *);
-
-#define __HAVE_ARCH_STRNCMP
-int strncmp(const char *, const char *, __kernel_size_t);
-
-#endif /* !EXPORT_SYMTAB_STROPS */
-
-#endif /* __KERNEL__ */
-
 #endif /* !(__SPARC_STRING_H__) */
index 5936b8ff3c050c15d873e2fc650185ede4912c1e..6b9ccb3086057a09feb565ade3c3f48859e3246f 100644 (file)
@@ -9,54 +9,10 @@
 #ifndef __SPARC64_STRING_H__
 #define __SPARC64_STRING_H__
 
-/* Really, userland/ksyms should not see any of this stuff. */
-
-#ifdef __KERNEL__
-
 #include <asm/asi.h>
 
-#ifndef EXPORT_SYMTAB_STROPS
-
-/* First the mem*() things. */
-#define __HAVE_ARCH_MEMMOVE
-void *memmove(void *, const void *, __kernel_size_t);
-
-#define __HAVE_ARCH_MEMCPY
-#define memcpy(t, f, n) __builtin_memcpy(t, f, n)
-
-#define __HAVE_ARCH_MEMSET
-#define memset(s, c, count) __builtin_memset(s, c, count)
-
-#define __HAVE_ARCH_MEMSCAN
-
-#undef memscan
-#define memscan(__arg0, __char, __arg2)                                        \
-({                                                                     \
-       void *__memscan_zero(void *, size_t);                           \
-       void *__memscan_generic(void *, int, size_t);                   \
-       void *__retval, *__addr = (__arg0);                             \
-       size_t __size = (__arg2);                                       \
-                                                                       \
-       if(__builtin_constant_p(__char) && !(__char))                   \
-               __retval = __memscan_zero(__addr, __size);              \
-       else                                                            \
-               __retval = __memscan_generic(__addr, (__char), __size); \
-                                                                       \
-       __retval;                                                       \
-})
-
-#define __HAVE_ARCH_MEMCMP
-int memcmp(const void *,const void *,__kernel_size_t);
-
 /* Now the str*() stuff... */
 #define __HAVE_ARCH_STRLEN
 __kernel_size_t strlen(const char *);
 
-#define __HAVE_ARCH_STRNCMP
-int strncmp(const char *, const char *, __kernel_size_t);
-
-#endif /* !EXPORT_SYMTAB_STROPS */
-
-#endif /* __KERNEL__ */
-
 #endif /* !(__SPARC64_STRING_H__) */
index fdb13327fded36a313b054783adf1654725d237f..fa3c02d411389e9f9ef118c8ccd8016c0a678559 100644 (file)
@@ -86,7 +86,7 @@ obj-y                     += auxio_$(BITS).o
 obj-$(CONFIG_SUN_PM)      += apc.o pmc.o
 
 obj-$(CONFIG_MODULES)     += module.o
-obj-$(CONFIG_MODULES)     += sparc_ksyms_$(BITS).o
+obj-$(CONFIG_MODULES)     += sparc_ksyms.o
 obj-$(CONFIG_SPARC_LED)   += led.o
 obj-$(CONFIG_KGDB)        += kgdb_$(BITS).o
 
index 07918ab3062e1a219ee456593044fb7ee61044e4..d85bdb9998193ce53d40e4108809de0241e90919 100644 (file)
@@ -29,6 +29,7 @@
 #include <asm/unistd.h>
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 #define curptr      g6
 
@@ -1207,6 +1208,8 @@ delay_continue:
        
        ret
        restore
+EXPORT_SYMBOL(__udelay)
+EXPORT_SYMBOL(__ndelay)
 
        /* Handle a software breakpoint */
        /* We have to inform parent that child has stopped */
index 3d92c0a8f6c4928c95046eb108f5b9a9cc8c8bf8..7bb317b87ddeb4180a1381faa0c89140dc4b7e80 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/thread_info.h>   /* TI_UWINMASK */
 #include <asm/errno.h>
 #include <asm/pgtsrmmu.h>      /* SRMMU_PGDIR_SHIFT */
+#include <asm/export.h>
 
        .data
 /* The following are used with the prom_vector node-ops to figure out
@@ -60,6 +61,7 @@ sun4e_notsup:
  */
        .globl empty_zero_page
 empty_zero_page:       .skip PAGE_SIZE
+EXPORT_SYMBOL(empty_zero_page)
 
        .global root_flags
        .global ram_flags
@@ -813,3 +815,4 @@ lvl14_save:
 __ret_efault:
         ret
          restore %g0, -EFAULT, %o0
+EXPORT_SYMBOL(__ret_efault)
index a076b4249e622a4ebfe47eab6c9bcd7cb98b36ce..beba6c11554cb835517f911893819fffd0d8ad9e 100644 (file)
@@ -32,7 +32,8 @@
 #include <asm/estate.h>
 #include <asm/sfafsr.h>
 #include <asm/unistd.h>
-       
+#include <asm/export.h>
+
 /* This section from from _start to sparc64_boot_end should fit into
  * 0x0000000000404000 to 0x0000000000408000.
  */
@@ -143,6 +144,7 @@ prom_cpu_compatible:
        .skip   64
 prom_root_node:
        .word   0
+EXPORT_SYMBOL(prom_root_node)
 prom_mmu_ihandle_cache:
        .word   0
 prom_boot_mapped_pc:
@@ -158,6 +160,7 @@ is_sun4v:
        .word   0
 sun4v_chip_type:
        .word   SUN4V_CHIP_INVALID
+EXPORT_SYMBOL(sun4v_chip_type)
 1:
        rd      %pc, %l0
 
@@ -920,6 +923,7 @@ swapper_4m_tsb:
        .globl  prom_tba, tlb_type
 prom_tba:      .xword  0
 tlb_type:      .word   0       /* Must NOT end up in BSS */
+EXPORT_SYMBOL(tlb_type)
        .section        ".fixup",#alloc,#execinstr
 
        .globl  __ret_efault, __retl_efault, __ret_one, __retl_one
@@ -927,6 +931,7 @@ ENTRY(__ret_efault)
        ret
         restore %g0, -EFAULT, %o0
 ENDPROC(__ret_efault)
+EXPORT_SYMBOL(__ret_efault)
 
 ENTRY(__retl_efault)
        retl
index 314dd0c9fc5b24e5c8064f92520cc37e7cdd3c47..e4e5b832fcb6f9313b353400e45a840afbd29e26 100644 (file)
@@ -15,6 +15,7 @@ __flushw_user:
 2:     retl
         nop
        .size   __flushw_user,.-__flushw_user
+EXPORT_SYMBOL(__flushw_user)
 
        /* Flush %fp and %i7 to the stack for all register
         * windows active inside of the cpu.  This allows
@@ -61,3 +62,4 @@ real_hard_smp_processor_id:
        .size           hard_smp_processor_id,.-hard_smp_processor_id
 #endif
        .size           real_hard_smp_processor_id,.-real_hard_smp_processor_id
+EXPORT_SYMBOL_GPL(real_hard_smp_processor_id)
index d127130bf4246032d39cf923248fce7eebf92867..4116ee5c77913221593421836eaae0803115a028 100644 (file)
@@ -343,6 +343,7 @@ ENTRY(sun4v_mach_set_watchdog)
 0:     retl
         nop
 ENDPROC(sun4v_mach_set_watchdog)
+EXPORT_SYMBOL(sun4v_mach_set_watchdog)
 
        /* No inputs and does not return.  */
 ENTRY(sun4v_mach_sir)
@@ -776,6 +777,7 @@ ENTRY(sun4v_niagara_getperf)
        retl
         nop
 ENDPROC(sun4v_niagara_getperf)
+EXPORT_SYMBOL(sun4v_niagara_getperf)
 
 ENTRY(sun4v_niagara_setperf)
        mov     HV_FAST_SET_PERFREG, %o5
@@ -783,6 +785,7 @@ ENTRY(sun4v_niagara_setperf)
        retl
         nop
 ENDPROC(sun4v_niagara_setperf)
+EXPORT_SYMBOL(sun4v_niagara_setperf)
 
 ENTRY(sun4v_niagara2_getperf)
        mov     %o0, %o4
@@ -792,6 +795,7 @@ ENTRY(sun4v_niagara2_getperf)
        retl
         nop
 ENDPROC(sun4v_niagara2_getperf)
+EXPORT_SYMBOL(sun4v_niagara2_getperf)
 
 ENTRY(sun4v_niagara2_setperf)
        mov     HV_FAST_N2_SET_PERFREG, %o5
@@ -799,6 +803,7 @@ ENTRY(sun4v_niagara2_setperf)
        retl
         nop
 ENDPROC(sun4v_niagara2_setperf)
+EXPORT_SYMBOL(sun4v_niagara2_setperf)
 
 ENTRY(sun4v_reboot_data_set)
        mov     HV_FAST_REBOOT_DATA_SET, %o5
index 9ddc4928a089b599568331792097c2bc35ea0be8..ac082dd8c67d5a4f1fbf527145b82bd251fbdc8d 100644 (file)
@@ -127,7 +127,8 @@ static int get_from_target(struct task_struct *target, unsigned long uaddr,
                if (copy_from_user(kbuf, (void __user *) uaddr, len))
                        return -EFAULT;
        } else {
-               int len2 = access_process_vm(target, uaddr, kbuf, len, 0);
+               int len2 = access_process_vm(target, uaddr, kbuf, len,
+                               FOLL_FORCE);
                if (len2 != len)
                        return -EFAULT;
        }
@@ -141,7 +142,8 @@ static int set_to_target(struct task_struct *target, unsigned long uaddr,
                if (copy_to_user((void __user *) uaddr, kbuf, len))
                        return -EFAULT;
        } else {
-               int len2 = access_process_vm(target, uaddr, kbuf, len, 1);
+               int len2 = access_process_vm(target, uaddr, kbuf, len,
+                               FOLL_FORCE | FOLL_WRITE);
                if (len2 != len)
                        return -EFAULT;
        }
@@ -505,7 +507,8 @@ static int genregs32_get(struct task_struct *target,
                                if (access_process_vm(target,
                                                      (unsigned long)
                                                      &reg_window[pos],
-                                                     k, sizeof(*k), 0)
+                                                     k, sizeof(*k),
+                                                     FOLL_FORCE)
                                    != sizeof(*k))
                                        return -EFAULT;
                                k++;
@@ -531,12 +534,14 @@ static int genregs32_get(struct task_struct *target,
                                if (access_process_vm(target,
                                                      (unsigned long)
                                                      &reg_window[pos],
-                                                     &reg, sizeof(reg), 0)
+                                                     &reg, sizeof(reg),
+                                                     FOLL_FORCE)
                                    != sizeof(reg))
                                        return -EFAULT;
                                if (access_process_vm(target,
                                                      (unsigned long) u,
-                                                     &reg, sizeof(reg), 1)
+                                                     &reg, sizeof(reg),
+                                                     FOLL_FORCE | FOLL_WRITE)
                                    != sizeof(reg))
                                        return -EFAULT;
                                pos++;
@@ -615,7 +620,8 @@ static int genregs32_set(struct task_struct *target,
                                                      (unsigned long)
                                                      &reg_window[pos],
                                                      (void *) k,
-                                                     sizeof(*k), 1)
+                                                     sizeof(*k),
+                                                     FOLL_FORCE | FOLL_WRITE)
                                    != sizeof(*k))
                                        return -EFAULT;
                                k++;
@@ -642,13 +648,15 @@ static int genregs32_set(struct task_struct *target,
                                if (access_process_vm(target,
                                                      (unsigned long)
                                                      u,
-                                                     &reg, sizeof(reg), 0)
+                                                     &reg, sizeof(reg),
+                                                     FOLL_FORCE)
                                    != sizeof(reg))
                                        return -EFAULT;
                                if (access_process_vm(target,
                                                      (unsigned long)
                                                      &reg_window[pos],
-                                                     &reg, sizeof(reg), 1)
+                                                     &reg, sizeof(reg),
+                                                     FOLL_FORCE | FOLL_WRITE)
                                    != sizeof(reg))
                                        return -EFAULT;
                                pos++;
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
new file mode 100644 (file)
index 0000000..09aa69e
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#include <linux/init.h>
+#include <linux/export.h>
+
+/* This is needed only for drivers/sbus/char/openprom.c */
+EXPORT_SYMBOL(saved_command_line);
diff --git a/arch/sparc/kernel/sparc_ksyms_32.c b/arch/sparc/kernel/sparc_ksyms_32.c
deleted file mode 100644 (file)
index bf4ccb1..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
- *
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- */
-
-#include <linux/module.h>
-
-#include <asm/pgtable.h>
-#include <asm/uaccess.h>
-#include <asm/delay.h>
-#include <asm/head.h>
-#include <asm/dma.h>
-
-struct poll {
-       int fd;
-       short events;
-       short revents;
-};
-
-/* from entry.S */
-EXPORT_SYMBOL(__udelay);
-EXPORT_SYMBOL(__ndelay);
-
-/* from head_32.S */
-EXPORT_SYMBOL(__ret_efault);
-EXPORT_SYMBOL(empty_zero_page);
-
-/* Exporting a symbol from /init/main.c */
-EXPORT_SYMBOL(saved_command_line);
diff --git a/arch/sparc/kernel/sparc_ksyms_64.c b/arch/sparc/kernel/sparc_ksyms_64.c
deleted file mode 100644 (file)
index 9e034f2..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/* arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
- *
- * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz)
- */
-
-#include <linux/export.h>
-#include <linux/pci.h>
-#include <linux/bitops.h>
-
-#include <asm/cpudata.h>
-#include <asm/uaccess.h>
-#include <asm/spitfire.h>
-#include <asm/oplib.h>
-#include <asm/hypervisor.h>
-#include <asm/cacheflush.h>
-
-struct poll {
-       int fd;
-       short events;
-       short revents;
-};
-
-/* from helpers.S */
-EXPORT_SYMBOL(__flushw_user);
-EXPORT_SYMBOL_GPL(real_hard_smp_processor_id);
-
-/* from head_64.S */
-EXPORT_SYMBOL(__ret_efault);
-EXPORT_SYMBOL(tlb_type);
-EXPORT_SYMBOL(sun4v_chip_type);
-EXPORT_SYMBOL(prom_root_node);
-
-/* from hvcalls.S */
-EXPORT_SYMBOL(sun4v_niagara_getperf);
-EXPORT_SYMBOL(sun4v_niagara_setperf);
-EXPORT_SYMBOL(sun4v_niagara2_getperf);
-EXPORT_SYMBOL(sun4v_niagara2_setperf);
-EXPORT_SYMBOL(sun4v_mach_set_watchdog);
-
-/* from hweight.S */
-EXPORT_SYMBOL(__arch_hweight8);
-EXPORT_SYMBOL(__arch_hweight16);
-EXPORT_SYMBOL(__arch_hweight32);
-EXPORT_SYMBOL(__arch_hweight64);
-
-/* from ffs_ffz.S */
-EXPORT_SYMBOL(ffs);
-EXPORT_SYMBOL(__ffs);
-
-/* Exporting a symbol from /init/main.c */
-EXPORT_SYMBOL(saved_command_line);
index 3269b0234093bfdbd6b6dcd22388cdc65a627d0a..885f00e81d1ab1f56cd4e47f788f79e653de9d08 100644 (file)
@@ -43,5 +43,4 @@ lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o hweight.o ffs.o
 
 obj-$(CONFIG_SPARC64) += iomap.o
 obj-$(CONFIG_SPARC32) += atomic32.o ucmpdi2.o
-obj-y                 += ksyms.o
 obj-$(CONFIG_SPARC64) += PeeCeeI.o
index 3e6209ebb7d7865fd62bb21df56dabda07fe4f5f..97e1b211090c2ac50a5194b418b091ff9e10f95d 100644 (file)
@@ -7,6 +7,7 @@
 #ifdef __KERNEL__
 #include <asm/visasm.h>
 #include <asm/asi.h>
+#include <asm/export.h>
 #define GLOBAL_SPARE   g7
 #else
 #define GLOBAL_SPARE   g5
@@ -567,3 +568,4 @@ FUNC_NAME:          /* %o0=dst, %o1=src, %o2=len */
         mov            EX_RETVAL(%o4), %o0
 
        .size           FUNC_NAME, .-FUNC_NAME
+EXPORT_SYMBOL(FUNC_NAME)
index 62c2647bd5cefaa832edf622c641bd0f3c17e8dc..1c7b6a39b9424acdc887c6640f3e4e7139cf5ed1 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/ptrace.h>
 #include <asm/visasm.h>
 #include <asm/thread_info.h>
+#include <asm/export.h>
 
        /* On entry: %o5=current FPRS value, %g7 is callers address */
        /* May clobber %o5, %g1, %g2, %g3, %g7, %icc, %xcc */
@@ -79,3 +80,4 @@ vis1: ldub            [%g6 + TI_FPSAVED], %g3
 80:    jmpl            %g7 + %g0, %g0
         nop
 ENDPROC(VISenter)
+EXPORT_SYMBOL(VISenter)
index 86f60de07b0a3526b620e6f76a94233b6e42e393..c8b1cf71bc73227e490c5662fe18759d58c012c9 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 ENTRY(__ashldi3)
@@ -33,3 +34,4 @@ ENTRY(__ashldi3)
        retl
         nop
 ENDPROC(__ashldi3)
+EXPORT_SYMBOL(__ashldi3)
index 6eb8ba2dd50e0de2a80a4c91bf734ac24017fd1c..4310256e796475b5e9ad3e1fb81b0839a521b027 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 ENTRY(__ashrdi3)
@@ -35,3 +36,4 @@ ENTRY(__ashrdi3)
        jmpl    %o7 + 8, %g0
         nop
 ENDPROC(__ashrdi3)
+EXPORT_SYMBOL(__ashrdi3)
index a5c5a0279cccc7b3487e9361500cf8162a3c89ad..1c6a1bde51388ea0402bc4271f796d5935dda4fb 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/linkage.h>
 #include <asm/asi.h>
 #include <asm/backoff.h>
+#include <asm/export.h>
 
        .text
 
@@ -29,6 +30,7 @@ ENTRY(atomic_##op) /* %o0 = increment, %o1 = atomic_ptr */            \
         nop;                                                           \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
 ENDPROC(atomic_##op);                                                  \
+EXPORT_SYMBOL(atomic_##op);
 
 #define ATOMIC_OP_RETURN(op)                                           \
 ENTRY(atomic_##op##_return) /* %o0 = increment, %o1 = atomic_ptr */    \
@@ -42,7 +44,8 @@ ENTRY(atomic_##op##_return) /* %o0 = increment, %o1 = atomic_ptr */   \
        retl;                                                           \
         sra    %g1, 0, %o0;                                            \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
-ENDPROC(atomic_##op##_return);
+ENDPROC(atomic_##op##_return);                                         \
+EXPORT_SYMBOL(atomic_##op##_return);
 
 #define ATOMIC_FETCH_OP(op)                                            \
 ENTRY(atomic_fetch_##op) /* %o0 = increment, %o1 = atomic_ptr */       \
@@ -56,7 +59,8 @@ ENTRY(atomic_fetch_##op) /* %o0 = increment, %o1 = atomic_ptr */      \
        retl;                                                           \
         sra    %g1, 0, %o0;                                            \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
-ENDPROC(atomic_fetch_##op);
+ENDPROC(atomic_fetch_##op);                                            \
+EXPORT_SYMBOL(atomic_fetch_##op);
 
 #define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_OP_RETURN(op) ATOMIC_FETCH_OP(op)
 
@@ -88,6 +92,7 @@ ENTRY(atomic64_##op) /* %o0 = increment, %o1 = atomic_ptr */          \
         nop;                                                           \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
 ENDPROC(atomic64_##op);                                                        \
+EXPORT_SYMBOL(atomic64_##op);
 
 #define ATOMIC64_OP_RETURN(op)                                         \
 ENTRY(atomic64_##op##_return) /* %o0 = increment, %o1 = atomic_ptr */  \
@@ -101,7 +106,8 @@ ENTRY(atomic64_##op##_return) /* %o0 = increment, %o1 = atomic_ptr */       \
        retl;                                                           \
         op     %g1, %o0, %o0;                                          \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
-ENDPROC(atomic64_##op##_return);
+ENDPROC(atomic64_##op##_return);                                       \
+EXPORT_SYMBOL(atomic64_##op##_return);
 
 #define ATOMIC64_FETCH_OP(op)                                          \
 ENTRY(atomic64_fetch_##op) /* %o0 = increment, %o1 = atomic_ptr */     \
@@ -115,7 +121,8 @@ ENTRY(atomic64_fetch_##op) /* %o0 = increment, %o1 = atomic_ptr */  \
        retl;                                                           \
         mov    %g1, %o0;                                               \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
-ENDPROC(atomic64_fetch_##op);
+ENDPROC(atomic64_fetch_##op);                                          \
+EXPORT_SYMBOL(atomic64_fetch_##op);
 
 #define ATOMIC64_OPS(op) ATOMIC64_OP(op) ATOMIC64_OP_RETURN(op) ATOMIC64_FETCH_OP(op)
 
@@ -147,3 +154,4 @@ ENTRY(atomic64_dec_if_positive) /* %o0 = atomic_ptr */
         sub    %g1, 1, %o0
 2:     BACKOFF_SPIN(%o2, %o3, 1b)
 ENDPROC(atomic64_dec_if_positive)
+EXPORT_SYMBOL(atomic64_dec_if_positive)
index 36f72cc0e67e682dc347d7efe66ae7c7c2eb5658..7031bf1587cb6136569f9f56af3c502a77f1350f 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/linkage.h>
 #include <asm/asi.h>
 #include <asm/backoff.h>
+#include <asm/export.h>
 
        .text
 
@@ -29,6 +30,7 @@ ENTRY(test_and_set_bit)       /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(test_and_set_bit)
+EXPORT_SYMBOL(test_and_set_bit)
 
 ENTRY(test_and_clear_bit) /* %o0=nr, %o1=addr */
        BACKOFF_SETUP(%o3)
@@ -50,6 +52,7 @@ ENTRY(test_and_clear_bit) /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(test_and_clear_bit)
+EXPORT_SYMBOL(test_and_clear_bit)
 
 ENTRY(test_and_change_bit) /* %o0=nr, %o1=addr */
        BACKOFF_SETUP(%o3)
@@ -71,6 +74,7 @@ ENTRY(test_and_change_bit) /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(test_and_change_bit)
+EXPORT_SYMBOL(test_and_change_bit)
 
 ENTRY(set_bit) /* %o0=nr, %o1=addr */
        BACKOFF_SETUP(%o3)
@@ -90,6 +94,7 @@ ENTRY(set_bit) /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(set_bit)
+EXPORT_SYMBOL(set_bit)
 
 ENTRY(clear_bit) /* %o0=nr, %o1=addr */
        BACKOFF_SETUP(%o3)
@@ -109,6 +114,7 @@ ENTRY(clear_bit) /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(clear_bit)
+EXPORT_SYMBOL(clear_bit)
 
 ENTRY(change_bit) /* %o0=nr, %o1=addr */
        BACKOFF_SETUP(%o3)
@@ -128,3 +134,4 @@ ENTRY(change_bit) /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(change_bit)
+EXPORT_SYMBOL(change_bit)
index 3c771011ff4b036d04aa424e3861972d08317517..1f2692d59d18ebe4a206d6ae27d875b1292da852 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/linkage.h>
 #include <asm/page.h>
+#include <asm/export.h>
 
        /* Zero out 64 bytes of memory at (buf + offset).
         * Assumes %g1 contains zero.
@@ -64,6 +65,7 @@ ENTRY(bzero_1page)
        retl
         nop
 ENDPROC(bzero_1page)
+EXPORT_SYMBOL(bzero_1page)
 
 ENTRY(__copy_1page)
 /* NOTE: If you change the number of insns of this routine, please check
@@ -87,3 +89,4 @@ ENTRY(__copy_1page)
        retl
         nop
 ENDPROC(__copy_1page)
+EXPORT_SYMBOL(__copy_1page)
index 8c058114b64901ea507d70c8b1a0f59f2c50372f..3bb1914c4fa487aecd598e11b4bd74687287c441 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 
@@ -78,6 +79,8 @@ __bzero_done:
         mov            %o3, %o0
 ENDPROC(__bzero)
 ENDPROC(memset)
+EXPORT_SYMBOL(__bzero)
+EXPORT_SYMBOL(memset)
 
 #define EX_ST(x,y)             \
 98:    x,y;                    \
@@ -143,3 +146,4 @@ __clear_user_done:
        retl
         clr            %o0
 ENDPROC(__clear_user)
+EXPORT_SYMBOL(__clear_user)
index 0084c3361e15afa77530071ada7a6e30c3ece3b5..c9d8b62321116aa7f472904afae12793c57c0527 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include <asm/errno.h>
+#include <asm/export.h>
 
 #define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5)        \
        ldd     [buf + offset + 0x00], t0;                      \
@@ -104,6 +105,7 @@ csum_partial_fix_alignment:
         * buffer of size 0x20.  Follow the code path for that case.
         */
        .globl  csum_partial
+       EXPORT_SYMBOL(csum_partial)
 csum_partial:                  /* %o0=buf, %o1=len, %o2=sum */
        andcc   %o0, 0x7, %g0                           ! alignment problems?
        bne     csum_partial_fix_alignment              ! yep, handle it
@@ -335,6 +337,7 @@ cc_dword_align:
         */
        .align  8
        .globl  __csum_partial_copy_sparc_generic
+       EXPORT_SYMBOL(__csum_partial_copy_sparc_generic)
 __csum_partial_copy_sparc_generic:
                                        /* %o0=src, %o1=dest, %g1=len, %g7=sum */
        xor     %o0, %o1, %o4           ! get changing bits
index 1d230f693dc4b1806e4c80b8df2918abeee878b8..f6732174fe6bdcceeb4228e64a92e2357cd81e00 100644 (file)
@@ -13,6 +13,7 @@
  *     BSD4.4 portable checksum routine
  */
 
+#include <asm/export.h>
        .text
 
 csum_partial_fix_alignment:
@@ -37,6 +38,7 @@ csum_partial_fix_alignment:
 
        .align          32
        .globl          csum_partial
+       EXPORT_SYMBOL(csum_partial)
 csum_partial:          /* %o0=buff, %o1=len, %o2=sum */
        prefetch        [%o0 + 0x000], #n_reads
        clr             %o4
index 46272dfc26e81e9ea4882b6789636f70bd8c5c47..f30d6b78afbd658fbcb9009fe34f6ce0bc2e99e0 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/pgtable.h>
 #include <asm/spitfire.h>
 #include <asm/head.h>
+#include <asm/export.h>
 
        /* What we used to do was lock a TLB entry into a specific
         * TLB slot, clear the page with interrupts disabled, then
@@ -26,6 +27,7 @@
        .text
 
        .globl          _clear_page
+       EXPORT_SYMBOL(_clear_page)
 _clear_page:           /* %o0=dest */
        ba,pt           %xcc, clear_page_common
         clr            %o4
@@ -35,6 +37,7 @@ _clear_page:          /* %o0=dest */
         */
        .align          32
        .globl          clear_user_page
+       EXPORT_SYMBOL(clear_user_page)
 clear_user_page:       /* %o0=dest, %o1=vaddr */
        lduw            [%g6 + TI_PRE_COUNT], %o2
        sethi           %hi(PAGE_OFFSET), %g2
index 302c0e60dc2ceb48a3212840ac7891f337419d14..482de093bdaeb3f33c1b9b2ed51396df2a10c1fe 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <linux/linkage.h>
 #include <asm/asi.h>
+#include <asm/export.h>
 
 #define XCC xcc
 
@@ -90,3 +91,4 @@ ENTRY(___copy_in_user)        /* %o0=dst, %o1=src, %o2=len */
        retl
         clr            %o0
 ENDPROC(___copy_in_user)
+EXPORT_SYMBOL(___copy_in_user)
index dd16c61f3263689f05b52d2da71f7c40685f1a0f..7197b72508951b68b7a1cc91087e29675bea183f 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/pgtable.h>
 #include <asm/spitfire.h>
 #include <asm/head.h>
+#include <asm/export.h>
 
        /* What we used to do was lock a TLB entry into a specific
         * TLB slot, clear the page with interrupts disabled, then
@@ -44,6 +45,7 @@
        .align          32
        .globl          copy_user_page
        .type           copy_user_page,#function
+       EXPORT_SYMBOL(copy_user_page)
 copy_user_page:                /* %o0=dest, %o1=src, %o2=vaddr */
        lduw            [%g6 + TI_PRE_COUNT], %o4
        sethi           %hi(PAGE_OFFSET), %g2
index ef095b6c43b157dc7ad076b641893b59e25775de..cea644dc67a628e8db216d6d5934fad2ec15e899 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/asmmacro.h>
 #include <asm/page.h>
 #include <asm/thread_info.h>
+#include <asm/export.h>
 
 /* Work around cpp -rob */
 #define ALLOC #alloc
 __copy_user_begin:
 
        .globl  __copy_user
+       EXPORT_SYMBOL(__copy_user)
 dword_align:
        andcc   %o1, 1, %g0
        be      4f
index e566c770a0f6372de5e4e7856ff520a9ac8920ac..0ecbafc30fd00e21cd7bd453d5e09d99349b8a82 100644 (file)
@@ -3,6 +3,8 @@
  * Copyright (C) 2005 David S. Miller <davem@davemloft.net>
  */
 
+#include <asm/export.h>
+
 #ifdef __KERNEL__
 #define GLOBAL_SPARE   %g7
 #else
@@ -63,6 +65,7 @@
         add            %o5, %o4, %o4
 
        .globl          FUNC_NAME
+       EXPORT_SYMBOL(FUNC_NAME)
 FUNC_NAME:             /* %o0=src, %o1=dst, %o2=len, %o3=sum */
        LOAD(prefetch, %o0 + 0x000, #n_reads)
        xor             %o0, %o1, %g1
index 9614b48b6ef839fe52d8dcb463419634fe2d5770..a2b5a976be33612cce76a5b76c531b5bd1f06adb 100644 (file)
@@ -17,6 +17,7 @@ along with GNU CC; see the file COPYING.  If not, write to
 the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
+#include <asm/export.h>
        .text
        .align 4
        .globl __divdi3
@@ -279,3 +280,4 @@ __divdi3:
 .LL81:
        ret
        restore
+EXPORT_SYMBOL(__divdi3)
index b39389f6989954aa6d9c0455d0fb3e99216763fa..23aab144d28e5b67b4db11710c9afb43746af6d4 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .register       %g2,#scratch
 
@@ -65,6 +66,8 @@ ENTRY(__ffs)
         add    %o2, %g1, %o0
 ENDPROC(ffs)
 ENDPROC(__ffs)
+EXPORT_SYMBOL(__ffs)
+EXPORT_SYMBOL(ffs)
 
        .section        .popc_6insn_patch, "ax"
        .word           ffs
index 95414e0a6808d596f85c4809c519914499c28d1d..f9985f129fb68e2c599b944a3be5c0a4ebddb8b3 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
        .align  32
@@ -7,6 +8,7 @@ ENTRY(__arch_hweight8)
         nop
        nop
 ENDPROC(__arch_hweight8)
+EXPORT_SYMBOL(__arch_hweight8)
        .section        .popc_3insn_patch, "ax"
        .word           __arch_hweight8
        sllx            %o0, 64-8, %g1
@@ -19,6 +21,7 @@ ENTRY(__arch_hweight16)
         nop
        nop
 ENDPROC(__arch_hweight16)
+EXPORT_SYMBOL(__arch_hweight16)
        .section        .popc_3insn_patch, "ax"
        .word           __arch_hweight16
        sllx            %o0, 64-16, %g1
@@ -31,6 +34,7 @@ ENTRY(__arch_hweight32)
         nop
        nop
 ENDPROC(__arch_hweight32)
+EXPORT_SYMBOL(__arch_hweight32)
        .section        .popc_3insn_patch, "ax"
        .word           __arch_hweight32
        sllx            %o0, 64-32, %g1
@@ -43,6 +47,7 @@ ENTRY(__arch_hweight64)
         nop
        nop
 ENDPROC(__arch_hweight64)
+EXPORT_SYMBOL(__arch_hweight64)
        .section        .popc_3insn_patch, "ax"
        .word           __arch_hweight64
        retl
index 4742d59029ee3677d0a77cd121d4437bb823e151..5d61648b53dd29d62225fb6053a6d1a9e1d5d7e5 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 ENTRY(ip_fast_csum) /* %o0 = iph, %o1 = ihl */
@@ -31,3 +32,4 @@ ENTRY(ip_fast_csum) /* %o0 = iph, %o1 = ihl */
        retl
         and    %o2, %o1, %o0
 ENDPROC(ip_fast_csum)
+EXPORT_SYMBOL(ip_fast_csum)
diff --git a/arch/sparc/lib/ksyms.c b/arch/sparc/lib/ksyms.c
deleted file mode 100644 (file)
index de5e978..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Export of symbols defined in assembler
- */
-
-/* Tell string.h we don't want memcpy etc. as cpp defines */
-#define EXPORT_SYMTAB_STROPS
-
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/types.h>
-
-#include <asm/checksum.h>
-#include <asm/uaccess.h>
-#include <asm/ftrace.h>
-
-/* string functions */
-EXPORT_SYMBOL(strlen);
-EXPORT_SYMBOL(strncmp);
-
-/* mem* functions */
-extern void *__memscan_zero(void *, size_t);
-extern void *__memscan_generic(void *, int, size_t);
-extern void *__bzero(void *, size_t);
-
-EXPORT_SYMBOL(memscan);
-EXPORT_SYMBOL(__memscan_zero);
-EXPORT_SYMBOL(__memscan_generic);
-EXPORT_SYMBOL(memcmp);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(__bzero);
-
-/* Networking helper routines. */
-EXPORT_SYMBOL(csum_partial);
-
-#ifdef CONFIG_MCOUNT
-EXPORT_SYMBOL(_mcount);
-#endif
-
-/*
- * sparc
- */
-#ifdef CONFIG_SPARC32
-extern int __ashrdi3(int, int);
-extern int __ashldi3(int, int);
-extern int __lshrdi3(int, int);
-extern int __muldi3(int, int);
-extern int __divdi3(int, int);
-
-extern void (*__copy_1page)(void *, const void *);
-extern void (*bzero_1page)(void *);
-
-extern void ___rw_read_enter(void);
-extern void ___rw_read_try(void);
-extern void ___rw_read_exit(void);
-extern void ___rw_write_enter(void);
-
-/* Networking helper routines. */
-EXPORT_SYMBOL(__csum_partial_copy_sparc_generic);
-
-/* Special internal versions of library functions. */
-EXPORT_SYMBOL(__copy_1page);
-EXPORT_SYMBOL(__memmove);
-EXPORT_SYMBOL(bzero_1page);
-
-/* Moving data to/from/in userspace. */
-EXPORT_SYMBOL(__copy_user);
-
-/* Used by asm/spinlock.h */
-#ifdef CONFIG_SMP
-EXPORT_SYMBOL(___rw_read_enter);
-EXPORT_SYMBOL(___rw_read_try);
-EXPORT_SYMBOL(___rw_read_exit);
-EXPORT_SYMBOL(___rw_write_enter);
-#endif
-
-EXPORT_SYMBOL(__ashrdi3);
-EXPORT_SYMBOL(__ashldi3);
-EXPORT_SYMBOL(__lshrdi3);
-EXPORT_SYMBOL(__muldi3);
-EXPORT_SYMBOL(__divdi3);
-#endif
-
-/*
- * sparc64
- */
-#ifdef CONFIG_SPARC64
-/* Networking helper routines. */
-EXPORT_SYMBOL(csum_partial_copy_nocheck);
-EXPORT_SYMBOL(__csum_partial_copy_from_user);
-EXPORT_SYMBOL(__csum_partial_copy_to_user);
-EXPORT_SYMBOL(ip_fast_csum);
-
-/* Moving data to/from/in userspace. */
-EXPORT_SYMBOL(___copy_to_user);
-EXPORT_SYMBOL(___copy_from_user);
-EXPORT_SYMBOL(___copy_in_user);
-EXPORT_SYMBOL(__clear_user);
-
-/* Atomic counter implementation. */
-#define ATOMIC_OP(op)                                                  \
-EXPORT_SYMBOL(atomic_##op);                                            \
-EXPORT_SYMBOL(atomic64_##op);
-
-#define ATOMIC_OP_RETURN(op)                                           \
-EXPORT_SYMBOL(atomic_##op##_return);                                   \
-EXPORT_SYMBOL(atomic64_##op##_return);
-
-#define ATOMIC_FETCH_OP(op)                                            \
-EXPORT_SYMBOL(atomic_fetch_##op);                                      \
-EXPORT_SYMBOL(atomic64_fetch_##op);
-
-#define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_OP_RETURN(op) ATOMIC_FETCH_OP(op)
-
-ATOMIC_OPS(add)
-ATOMIC_OPS(sub)
-
-#undef ATOMIC_OPS
-#define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_FETCH_OP(op)
-
-ATOMIC_OPS(and)
-ATOMIC_OPS(or)
-ATOMIC_OPS(xor)
-
-#undef ATOMIC_OPS
-#undef ATOMIC_FETCH_OP
-#undef ATOMIC_OP_RETURN
-#undef ATOMIC_OP
-
-EXPORT_SYMBOL(atomic64_dec_if_positive);
-
-/* Atomic bit operations. */
-EXPORT_SYMBOL(test_and_set_bit);
-EXPORT_SYMBOL(test_and_clear_bit);
-EXPORT_SYMBOL(test_and_change_bit);
-EXPORT_SYMBOL(set_bit);
-EXPORT_SYMBOL(clear_bit);
-EXPORT_SYMBOL(change_bit);
-
-/* Special internal versions of library functions. */
-EXPORT_SYMBOL(_clear_page);
-EXPORT_SYMBOL(clear_user_page);
-EXPORT_SYMBOL(copy_user_page);
-
-/* RAID code needs this */
-void VISenter(void);
-EXPORT_SYMBOL(VISenter);
-
-extern void xor_vis_2(unsigned long, unsigned long *, unsigned long *);
-extern void xor_vis_3(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *);
-extern void xor_vis_4(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *, unsigned long *);
-extern void xor_vis_5(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *, unsigned long *, unsigned long *);
-EXPORT_SYMBOL(xor_vis_2);
-EXPORT_SYMBOL(xor_vis_3);
-EXPORT_SYMBOL(xor_vis_4);
-EXPORT_SYMBOL(xor_vis_5);
-
-extern void xor_niagara_2(unsigned long, unsigned long *, unsigned long *);
-extern void xor_niagara_3(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *);
-extern void xor_niagara_4(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *, unsigned long *);
-extern void xor_niagara_5(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *, unsigned long *, unsigned long *);
-
-EXPORT_SYMBOL(xor_niagara_2);
-EXPORT_SYMBOL(xor_niagara_3);
-EXPORT_SYMBOL(xor_niagara_4);
-EXPORT_SYMBOL(xor_niagara_5);
-#endif
index 64f53f2b673de1cb91a80dc2eb3929ac65ad2db8..f38c4e59d078fefec684bc68bad68865e1945662 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/psr.h>
 #include <asm/smp.h>
 #include <asm/spinlock.h>
+#include <asm/export.h>
 
        .text
        .align  4
@@ -48,6 +49,7 @@ ___rw_write_enter_spin_on_wlock:
         ld     [%g1], %g2
 
        .globl  ___rw_read_enter
+EXPORT_SYMBOL(___rw_read_enter)
 ___rw_read_enter:
        orcc    %g2, 0x0, %g0
        bne,a   ___rw_read_enter_spin_on_wlock
@@ -59,6 +61,7 @@ ___rw_read_enter:
         mov    %g4, %o7
 
        .globl  ___rw_read_exit
+EXPORT_SYMBOL(___rw_read_exit)
 ___rw_read_exit:
        orcc    %g2, 0x0, %g0
        bne,a   ___rw_read_exit_spin_on_wlock
@@ -70,6 +73,7 @@ ___rw_read_exit:
         mov    %g4, %o7
 
        .globl  ___rw_read_try
+EXPORT_SYMBOL(___rw_read_try)
 ___rw_read_try:
        orcc    %g2, 0x0, %g0
        bne     ___rw_read_try_spin_on_wlock
@@ -81,6 +85,7 @@ ___rw_read_try:
         mov    %g4, %o7
 
        .globl  ___rw_write_enter
+EXPORT_SYMBOL(___rw_write_enter)
 ___rw_write_enter:
        orcc    %g2, 0x0, %g0
        bne     ___rw_write_enter_spin_on_wlock
index 60ebc7cdbee04a89b772b4ea28d515f86a5cae0e..c9b9373f8d8104587dc5b001f207ddc880775689 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 ENTRY(__lshrdi3)
        cmp     %o2, 0
@@ -25,3 +26,4 @@ ENTRY(__lshrdi3)
        retl 
         nop 
 ENDPROC(__lshrdi3)
+EXPORT_SYMBOL(__lshrdi3)
index 0b0ed4d34219bc6dfccd1a0ab337727b2417a5b0..194f383611c02f815431f90dfd662c3718f3b4a8 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 /*
  * This is the main variant and is called by C code.  GCC's -pg option
@@ -16,6 +17,7 @@
        .align          32
        .globl          _mcount
        .type           _mcount,#function
+       EXPORT_SYMBOL(_mcount)
        .globl          mcount
        .type           mcount,#function
 _mcount:
index efa106c41ed0af7777d4a6a8b70ca9585d9b694d..cee7f30dbb61352cb33552ed1240821269c76dfa 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/linkage.h>
 #include <asm/asm.h>
+#include <asm/export.h>
 
        .text
 ENTRY(memcmp)
@@ -25,3 +26,4 @@ ENTRY(memcmp)
 2:     retl
         mov    0, %o0
 ENDPROC(memcmp)
+EXPORT_SYMBOL(memcmp)
index 4d8c497517bd6f3f17110f2a3b4e6b6fe204e39e..8913feaa7ac7e1bb0ec677a531c58f6cc946b948 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
+#include <asm/export.h>
 #define FUNC(x)                \
        .globl  x;              \
        .type   x,@function;    \
@@ -58,93 +59,11 @@ x:
        stb     %t0, [%dst - (offset) - 0x02]; \
        stb     %t1, [%dst - (offset) - 0x01];
 
-/* Both these macros have to start with exactly the same insn */
-#define RMOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
-       ldd     [%src - (offset) - 0x20], %t0; \
-       ldd     [%src - (offset) - 0x18], %t2; \
-       ldd     [%src - (offset) - 0x10], %t4; \
-       ldd     [%src - (offset) - 0x08], %t6; \
-       st      %t0, [%dst - (offset) - 0x20]; \
-       st      %t1, [%dst - (offset) - 0x1c]; \
-       st      %t2, [%dst - (offset) - 0x18]; \
-       st      %t3, [%dst - (offset) - 0x14]; \
-       st      %t4, [%dst - (offset) - 0x10]; \
-       st      %t5, [%dst - (offset) - 0x0c]; \
-       st      %t6, [%dst - (offset) - 0x08]; \
-       st      %t7, [%dst - (offset) - 0x04];
-
-#define RMOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
-       ldd     [%src - (offset) - 0x20], %t0; \
-       ldd     [%src - (offset) - 0x18], %t2; \
-       ldd     [%src - (offset) - 0x10], %t4; \
-       ldd     [%src - (offset) - 0x08], %t6; \
-       std     %t0, [%dst - (offset) - 0x20]; \
-       std     %t2, [%dst - (offset) - 0x18]; \
-       std     %t4, [%dst - (offset) - 0x10]; \
-       std     %t6, [%dst - (offset) - 0x08];
-
-#define RMOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
-       ldd     [%src + (offset) + 0x00], %t0; \
-       ldd     [%src + (offset) + 0x08], %t2; \
-       st      %t0, [%dst + (offset) + 0x00]; \
-       st      %t1, [%dst + (offset) + 0x04]; \
-       st      %t2, [%dst + (offset) + 0x08]; \
-       st      %t3, [%dst + (offset) + 0x0c];
-
-#define RMOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
-       ldub    [%src + (offset) + 0x00], %t0; \
-       ldub    [%src + (offset) + 0x01], %t1; \
-       stb     %t0, [%dst + (offset) + 0x00]; \
-       stb     %t1, [%dst + (offset) + 0x01];
-
-#define SMOVE_CHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, prev, shil, shir, offset2) \
-       ldd     [%src + (offset) + 0x00], %t0; \
-       ldd     [%src + (offset) + 0x08], %t2; \
-       srl     %t0, shir, %t5; \
-       srl     %t1, shir, %t6; \
-       sll     %t0, shil, %t0; \
-       or      %t5, %prev, %t5; \
-       sll     %t1, shil, %prev; \
-       or      %t6, %t0, %t0; \
-       srl     %t2, shir, %t1; \
-       srl     %t3, shir, %t6; \
-       sll     %t2, shil, %t2; \
-       or      %t1, %prev, %t1; \
-       std     %t4, [%dst + (offset) + (offset2) - 0x04]; \
-       std     %t0, [%dst + (offset) + (offset2) + 0x04]; \
-       sll     %t3, shil, %prev; \
-       or      %t6, %t2, %t4;
-
-#define SMOVE_ALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, prev, shil, shir, offset2) \
-       ldd     [%src + (offset) + 0x00], %t0; \
-       ldd     [%src + (offset) + 0x08], %t2; \
-       srl     %t0, shir, %t4; \
-       srl     %t1, shir, %t5; \
-       sll     %t0, shil, %t6; \
-       or      %t4, %prev, %t0; \
-       sll     %t1, shil, %prev; \
-       or      %t5, %t6, %t1; \
-       srl     %t2, shir, %t4; \
-       srl     %t3, shir, %t5; \
-       sll     %t2, shil, %t6; \
-       or      %t4, %prev, %t2; \
-       sll     %t3, shil, %prev; \
-       or      %t5, %t6, %t3; \
-       std     %t0, [%dst + (offset) + (offset2) + 0x00]; \
-       std     %t2, [%dst + (offset) + (offset2) + 0x08];
-
        .text
        .align  4
 
-0:
-       retl
-        nop            ! Only bcopy returns here and it retuns void...
-
-#ifdef __KERNEL__
-FUNC(amemmove)
-FUNC(__memmove)
-#endif
 FUNC(memmove)
+EXPORT_SYMBOL(memmove)
        cmp             %o0, %o1
        mov             %o0, %g7
        bleu            9f
@@ -202,6 +121,7 @@ FUNC(memmove)
         add            %o0, 2, %o0
 
 FUNC(memcpy)   /* %o0=dst %o1=src %o2=len */
+EXPORT_SYMBOL(memcpy)
 
        sub             %o0, %o1, %o4
        mov             %o0, %g7
index 857ad4f8905f942f44ac40770c9277323e4e3c09..012cdb6ca4677b09da150b3f8b61ae98879b5952 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 ENTRY(memmove) /* o0=dst o1=src o2=len */
@@ -57,3 +58,4 @@ ENTRY(memmove) /* o0=dst o1=src o2=len */
         stb            %g7, [%o0 - 0x1]
        ba,a,pt         %xcc, 99b
 ENDPROC(memmove)
+EXPORT_SYMBOL(memmove)
index 4ff1657dfc246a65a432cf228ba3088d853db2c4..51ce690c42a8ec3a06e43edb92b098de36db0b4d 100644 (file)
@@ -4,6 +4,8 @@
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <asm/export.h>
+
 /* In essence, this is just a fancy strlen. */
 
 #define LO_MAGIC 0x01010101
@@ -13,6 +15,8 @@
        .align  4
        .globl  __memscan_zero, __memscan_generic
        .globl  memscan
+EXPORT_SYMBOL(__memscan_zero)
+EXPORT_SYMBOL(__memscan_generic)
 __memscan_zero:
        /* %o0 = addr, %o1 = size */
        cmp     %o1, 0
index 5686dfa5dc1595ef14c47dfc5664b44a537f572b..daa96f4b03e6007fbc415a70514f3f56be8a4ef1 100644 (file)
@@ -5,6 +5,8 @@
  * Copyright (C) 1998 David S. Miller (davem@redhat.com)
  */
 
+       #include <asm/export.h>
+
 #define HI_MAGIC       0x8080808080808080
 #define LO_MAGIC       0x0101010101010101
 #define ASI_PL         0x88
@@ -13,6 +15,8 @@
        .align  32
        .globl          __memscan_zero, __memscan_generic
        .globl          memscan
+       EXPORT_SYMBOL(__memscan_zero)
+       EXPORT_SYMBOL(__memscan_generic)
 
 __memscan_zero:
        /* %o0 = bufp, %o1 = size */
index f75e6906df146aae9a99cc83c6750903f9853783..bb539b42b088ace45ecf2d1419ee808986f12d72 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
 /* Work around cpp -rob */
 #define ALLOC #alloc
@@ -63,6 +64,8 @@ __bzero_begin:
 
        .globl  __bzero
        .globl  memset
+       EXPORT_SYMBOL(__bzero)
+       EXPORT_SYMBOL(memset)
        .globl  __memset_start, __memset_end
 __memset_start:
 memset:
index 9794939d1c12626187016451fd803d7653523d0a..17a0f49aef3c0d18d1e1e03d44c1f664c3384ee4 100644 (file)
@@ -17,6 +17,7 @@ along with GNU CC; see the file COPYING.  If not, write to
 the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
+#include <asm/export.h>
        .text
        .align 4
        .globl __muldi3
@@ -74,3 +75,4 @@ __muldi3:
        add  %l2, %l0, %i0
        ret 
        restore  %g0, %l3, %o1
+EXPORT_SYMBOL(__muldi3)
index 536f83507fbff1dc669f998f5e6b7336812225a5..ca0e7077e87136fae3875839b216b9fd80694fbb 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/linkage.h>
 #include <asm/asm.h>
+#include <asm/export.h>
 
 #define LO_MAGIC 0x01010101
 #define HI_MAGIC 0x80808080
@@ -78,3 +79,4 @@ ENTRY(strlen)
        retl
         mov    2, %o0
 ENDPROC(strlen)
+EXPORT_SYMBOL(strlen)
index c0d1b568c1c561f443356678d06ae6b7d4831053..e3fe014813af85bf1040fbef9165dadbb66f25f2 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 ENTRY(strncmp)
@@ -116,3 +117,4 @@ ENTRY(strncmp)
        retl
         sub    %o3, %o0, %o0
 ENDPROC(strncmp)
+EXPORT_SYMBOL(strncmp)
index 0656627166f38e3070731893beeba2acbad1b571..efb5f884330d73538f6ce74b09ace90076782023 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/linkage.h>
 #include <asm/asi.h>
+#include <asm/export.h>
 
        .text
 ENTRY(strncmp)
@@ -28,3 +29,4 @@ ENTRY(strncmp)
        retl
         clr    %o0
 ENDPROC(strncmp)
+EXPORT_SYMBOL(strncmp)
index 2c05641c326390949f51749e01cedc1b36002015..45a49cb618b52bea832ee2bd128406ea8c20d500 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/asi.h>
 #include <asm/dcu.h>
 #include <asm/spitfire.h>
+#include <asm/export.h>
 
 /*
  *     Requirements:
@@ -90,6 +91,7 @@ ENTRY(xor_vis_2)
        retl
          wr    %g0, 0, %fprs
 ENDPROC(xor_vis_2)
+EXPORT_SYMBOL(xor_vis_2)
 
 ENTRY(xor_vis_3)
        rd      %fprs, %o5
@@ -156,6 +158,7 @@ ENTRY(xor_vis_3)
        retl
         wr     %g0, 0, %fprs
 ENDPROC(xor_vis_3)
+EXPORT_SYMBOL(xor_vis_3)
 
 ENTRY(xor_vis_4)
        rd      %fprs, %o5
@@ -241,6 +244,7 @@ ENTRY(xor_vis_4)
        retl
         wr     %g0, 0, %fprs
 ENDPROC(xor_vis_4)
+EXPORT_SYMBOL(xor_vis_4)
 
 ENTRY(xor_vis_5)
        save    %sp, -192, %sp
@@ -347,6 +351,7 @@ ENTRY(xor_vis_5)
        ret
         restore
 ENDPROC(xor_vis_5)
+EXPORT_SYMBOL(xor_vis_5)
 
        /* Niagara versions. */
 ENTRY(xor_niagara_2) /* %o0=bytes, %o1=dest, %o2=src */
@@ -393,6 +398,7 @@ ENTRY(xor_niagara_2) /* %o0=bytes, %o1=dest, %o2=src */
        ret
         restore
 ENDPROC(xor_niagara_2)
+EXPORT_SYMBOL(xor_niagara_2)
 
 ENTRY(xor_niagara_3) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2 */
        save            %sp, -192, %sp
@@ -454,6 +460,7 @@ ENTRY(xor_niagara_3) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2 */
        ret
         restore
 ENDPROC(xor_niagara_3)
+EXPORT_SYMBOL(xor_niagara_3)
 
 ENTRY(xor_niagara_4) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3 */
        save            %sp, -192, %sp
@@ -536,6 +543,7 @@ ENTRY(xor_niagara_4) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3 */
        ret
         restore
 ENDPROC(xor_niagara_4)
+EXPORT_SYMBOL(xor_niagara_4)
 
 ENTRY(xor_niagara_5) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3, %o5=src4 */
        save            %sp, -192, %sp
@@ -634,3 +642,4 @@ ENTRY(xor_niagara_5) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3, %o5=s
        ret
         restore
 ENDPROC(xor_niagara_5)
+EXPORT_SYMBOL(xor_niagara_5)
index 4e06750a5d295649660ff4ca0998eb03b00da9e9..cd0e32bbcb1de0f6b16bce4ccd3f8b89acaa18b9 100644 (file)
@@ -238,7 +238,8 @@ slow:
                pages += nr;
 
                ret = get_user_pages_unlocked(start,
-                       (end - start) >> PAGE_SHIFT, write, 0, pages);
+                       (end - start) >> PAGE_SHIFT, pages,
+                       write ? FOLL_WRITE : 0);
 
                /* Have to be a bit careful with return values */
                if (nr > 0) {
index b75a8bcd2d23cced23df9bade46e6304499d926b..21b352a11b493f4868b1a091dc416c7e2279ad34 100644 (file)
@@ -44,6 +44,7 @@
 #include <asm/alternative-asm.h>
 #include <asm/asm.h>
 #include <asm/smap.h>
+#include <asm/export.h>
 
        .section .entry.text, "ax"
 
@@ -991,6 +992,7 @@ trace:
        jmp     ftrace_stub
 END(mcount)
 #endif /* CONFIG_DYNAMIC_FTRACE */
+EXPORT_SYMBOL(mcount)
 #endif /* CONFIG_FUNCTION_TRACER */
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
index c98ec2efd7500d9e7673fc1f215e3e561afd8641..ef766a358b37dd355e8d5d75c10a61d47a83e04f 100644 (file)
@@ -35,6 +35,7 @@
 #include <asm/asm.h>
 #include <asm/smap.h>
 #include <asm/pgtable_types.h>
+#include <asm/export.h>
 #include <linux/err.h>
 
 /* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this.  */
@@ -875,6 +876,7 @@ ENTRY(native_load_gs_index)
        popfq
        ret
 END(native_load_gs_index)
+EXPORT_SYMBOL(native_load_gs_index)
 
        _ASM_EXTABLE(.Lgs_change, bad_gs)
        .section .fixup, "ax"
index ff6ef7b3082237e5d223de9a9986202761566d17..2b361854254414662c17b531e2cacb8d5bb696b9 100644 (file)
 380    i386    pkey_mprotect           sys_pkey_mprotect
 381    i386    pkey_alloc              sys_pkey_alloc
 382    i386    pkey_free               sys_pkey_free
-#383   i386    pkey_get                sys_pkey_get
-#384   i386    pkey_set                sys_pkey_set
index 2f024d02511da47e12cacfbeea9826c440de8f9e..e93ef0b38db8e16a38f83e2e3f08dfb8d5fff4a0 100644 (file)
 329    common  pkey_mprotect           sys_pkey_mprotect
 330    common  pkey_alloc              sys_pkey_alloc
 331    common  pkey_free               sys_pkey_free
-#332   common  pkey_get                sys_pkey_get
-#333   common  pkey_set                sys_pkey_set
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
index e5a17114a8c4f917b56819bc6f7ff80ff0ac4798..fee6bc79b987faf56353ee367ca2aac61a87d49a 100644 (file)
@@ -6,6 +6,7 @@
  */
        #include <linux/linkage.h>
        #include <asm/asm.h>
+       #include <asm/export.h>
 
        /* put return address in eax (arg1) */
        .macro THUNK name, func, put_ret_addr_in_eax=0
@@ -36,5 +37,7 @@
 #ifdef CONFIG_PREEMPT
        THUNK ___preempt_schedule, preempt_schedule
        THUNK ___preempt_schedule_notrace, preempt_schedule_notrace
+       EXPORT_SYMBOL(___preempt_schedule)
+       EXPORT_SYMBOL(___preempt_schedule_notrace)
 #endif
 
index 627ecbcb2e6267796b634e05b16873285300a977..be36bf4e0957ec0df1437407fa98800cb7869737 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/linkage.h>
 #include "calling.h"
 #include <asm/asm.h>
+#include <asm/export.h>
 
        /* rdi: arg1 ... normal C conventions. rax is saved/restored. */
        .macro THUNK name, func, put_ret_addr_in_rdi=0
@@ -49,6 +50,8 @@
 #ifdef CONFIG_PREEMPT
        THUNK ___preempt_schedule, preempt_schedule
        THUNK ___preempt_schedule_notrace, preempt_schedule_notrace
+       EXPORT_SYMBOL(___preempt_schedule)
+       EXPORT_SYMBOL(___preempt_schedule_notrace)
 #endif
 
 #if defined(CONFIG_TRACE_IRQFLAGS) \
index a3a9eb84b5cf16ffebd5bf46c69037db4d1e3794..eab0915f59951f2729d212bb96af76785360eff0 100644 (file)
@@ -3898,6 +3898,7 @@ __init int intel_pmu_init(void)
                break;
 
        case INTEL_FAM6_XEON_PHI_KNL:
+       case INTEL_FAM6_XEON_PHI_KNM:
                memcpy(hw_cache_event_ids,
                       slm_hw_cache_event_ids, sizeof(hw_cache_event_ids));
                memcpy(hw_cache_extra_regs,
@@ -3912,7 +3913,7 @@ __init int intel_pmu_init(void)
                x86_pmu.flags |= PMU_FL_HAS_RSP_1;
                x86_pmu.flags |= PMU_FL_NO_HT_SHARING;
 
-               pr_cont("Knights Landing events, ");
+               pr_cont("Knights Landing/Mill events, ");
                break;
 
        case INTEL_FAM6_SKYLAKE_MOBILE:
index fc6cf21c535e19f4c07deccf4a6444199e5ae39c..81b321ace8e0194d3ce18b29fcb42c20b834a918 100644 (file)
@@ -458,8 +458,8 @@ void intel_pmu_lbr_del(struct perf_event *event)
        if (!x86_pmu.lbr_nr)
                return;
 
-       if (branch_user_callstack(cpuc->br_sel) && event->ctx &&
-                                       event->ctx->task_ctx_data) {
+       if (branch_user_callstack(cpuc->br_sel) &&
+           event->ctx->task_ctx_data) {
                task_ctx = event->ctx->task_ctx_data;
                task_ctx->lbr_callstack_users--;
        }
index b0f0e835a770f7ee959681513bd7e41af39827a0..0a535cea8ff31adf6e06fc32ff763d423a73ab74 100644 (file)
@@ -763,6 +763,7 @@ static const struct x86_cpu_id rapl_cpu_match[] __initconst = {
        X86_RAPL_MODEL_MATCH(INTEL_FAM6_BROADWELL_XEON_D, hsw_rapl_init),
 
        X86_RAPL_MODEL_MATCH(INTEL_FAM6_XEON_PHI_KNL, knl_rapl_init),
+       X86_RAPL_MODEL_MATCH(INTEL_FAM6_XEON_PHI_KNM, knl_rapl_init),
 
        X86_RAPL_MODEL_MATCH(INTEL_FAM6_SKYLAKE_MOBILE,  skl_rapl_init),
        X86_RAPL_MODEL_MATCH(INTEL_FAM6_SKYLAKE_DESKTOP, skl_rapl_init),
index d9844cc74486e602c7b768e225856323a37025b8..efca2685d8767582e624d1c38779e5c96eef33b9 100644 (file)
@@ -1349,6 +1349,7 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = {
        X86_UNCORE_MODEL_MATCH(INTEL_FAM6_BROADWELL_X,    bdx_uncore_init),
        X86_UNCORE_MODEL_MATCH(INTEL_FAM6_BROADWELL_XEON_D, bdx_uncore_init),
        X86_UNCORE_MODEL_MATCH(INTEL_FAM6_XEON_PHI_KNL,   knl_uncore_init),
+       X86_UNCORE_MODEL_MATCH(INTEL_FAM6_XEON_PHI_KNM,   knl_uncore_init),
        X86_UNCORE_MODEL_MATCH(INTEL_FAM6_SKYLAKE_DESKTOP,skl_uncore_init),
        X86_UNCORE_MODEL_MATCH(INTEL_FAM6_SKYLAKE_MOBILE, skl_uncore_init),
        X86_UNCORE_MODEL_MATCH(INTEL_FAM6_SKYLAKE_X,      skx_uncore_init),
diff --git a/arch/x86/include/asm/export.h b/arch/x86/include/asm/export.h
new file mode 100644 (file)
index 0000000..138de56
--- /dev/null
@@ -0,0 +1,4 @@
+#ifdef CONFIG_64BIT
+#define KSYM_ALIGN 16
+#endif
+#include <asm-generic/export.h>
index 9ae5ab80a497c35a9fa3048c532bdb9239c395e1..34a46dc076d3610212e6f5c9f0abfb2ab9bc3629 100644 (file)
@@ -64,5 +64,6 @@
 /* Xeon Phi */
 
 #define INTEL_FAM6_XEON_PHI_KNL                0x57 /* Knights Landing */
+#define INTEL_FAM6_XEON_PHI_KNM                0x85 /* Knights Mill */
 
 #endif /* _ASM_X86_INTEL_FAMILY_H */
index 56f4c6676b29034830d4fdb9c7cdcedd0c14e2a1..78f3760ca1f2985cfcbb278d59fbe71f92f69ea7 100644 (file)
@@ -88,7 +88,6 @@
 
 #define MSR_IA32_RTIT_CTL              0x00000570
 #define MSR_IA32_RTIT_STATUS           0x00000571
-#define MSR_IA32_RTIT_STATUS           0x00000571
 #define MSR_IA32_RTIT_ADDR0_A          0x00000580
 #define MSR_IA32_RTIT_ADDR0_B          0x00000581
 #define MSR_IA32_RTIT_ADDR1_A          0x00000582
index e02e3f80d363bbb87d8db6c74b2a0d5370818419..84f58de08c2bdbff7ce98119ff2e360d7d5175a1 100644 (file)
@@ -521,7 +521,8 @@ do {                                                                        \
 static __always_inline bool x86_this_cpu_constant_test_bit(unsigned int nr,
                         const unsigned long __percpu *addr)
 {
-       unsigned long __percpu *a = (unsigned long *)addr + nr / BITS_PER_LONG;
+       unsigned long __percpu *a =
+               (unsigned long __percpu *)addr + nr / BITS_PER_LONG;
 
 #ifdef CONFIG_X86_64
        return ((1UL << (nr % BITS_PER_LONG)) & raw_cpu_read_8(*a)) != 0;
@@ -538,7 +539,7 @@ static inline bool x86_this_cpu_variable_test_bit(int nr,
        asm volatile("bt "__percpu_arg(2)",%1\n\t"
                        CC_SET(c)
                        : CC_OUT(c) (oldbit)
-                       : "m" (*(unsigned long *)addr), "Ir" (nr));
+                       : "m" (*(unsigned long __percpu *)addr), "Ir" (nr));
 
        return oldbit;
 }
index 3d33a719f5c1d8652be749c43ba4b7dc5560c4d5..a34e0d4b957d639afb5978863e57fefb74a173f2 100644 (file)
@@ -103,8 +103,10 @@ static inline bool __down_read_trylock(struct rw_semaphore *sem)
 ({                                                     \
        long tmp;                                       \
        struct rw_semaphore* ret;                       \
+       register void *__sp asm(_ASM_SP);               \
+                                                       \
        asm volatile("# beginning down_write\n\t"       \
-                    LOCK_PREFIX "  xadd      %1,(%3)\n\t"      \
+                    LOCK_PREFIX "  xadd      %1,(%4)\n\t"      \
                     /* adds 0xffff0001, returns the old value */ \
                     "  test " __ASM_SEL(%w1,%k1) "," __ASM_SEL(%w1,%k1) "\n\t" \
                     /* was the active mask 0 before? */\
@@ -112,7 +114,7 @@ static inline bool __down_read_trylock(struct rw_semaphore *sem)
                     "  call " slow_path "\n"           \
                     "1:\n"                             \
                     "# ending down_write"              \
-                    : "+m" (sem->count), "=d" (tmp), "=a" (ret)        \
+                    : "+m" (sem->count), "=d" (tmp), "=a" (ret), "+r" (__sp) \
                     : "a" (sem), "1" (RWSEM_ACTIVE_WRITE_BIAS) \
                     : "memory", "cc");                 \
        ret;                                            \
index 4dd5d500eb6024cc27492d49a2458213e44bdca6..79076d75bdbfde2aec556e14fd8622e7c017682a 100644 (file)
@@ -46,9 +46,7 @@ obj-$(CONFIG_MODIFY_LDT_SYSCALL)      += ldt.o
 obj-y                  += setup.o x86_init.o i8259.o irqinit.o jump_label.o
 obj-$(CONFIG_IRQ_WORK)  += irq_work.o
 obj-y                  += probe_roms.o
-obj-$(CONFIG_X86_32)   += i386_ksyms_32.o
-obj-$(CONFIG_X86_64)   += sys_x86_64.o x8664_ksyms_64.o
-obj-$(CONFIG_X86_64)   += mcount_64.o
+obj-$(CONFIG_X86_64)   += sys_x86_64.o mcount_64.o
 obj-$(CONFIG_X86_ESPFIX64)     += espfix_64.o
 obj-$(CONFIG_SYSFS)    += ksysfs.o
 obj-y                  += bootflag.o e820.o
index b85fe5f91c3fe4901cf766aa1f6996710f844b0d..90e8dde3ec26b1d97d10309d2d796489078b5cdf 100644 (file)
@@ -350,7 +350,7 @@ int __init sanitize_e820_map(struct e820entry *biosmap, int max_nr_map,
                 * continue building up new bios map based on this
                 * information
                 */
-               if (current_type != last_type) {
+               if (current_type != last_type || current_type == E820_PRAM) {
                        if (last_type != 0)      {
                                new_bios[new_bios_entry].size =
                                        change_point[chgidx]->addr - last_addr;
index 5f401262f12d08c50d6411d80de056ef87b6c6e9..b6b2f0264af36ac272537e5d6689a38361bb60d4 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/percpu.h>
 #include <asm/nops.h>
 #include <asm/bootparam.h>
+#include <asm/export.h>
 
 /* Physical address */
 #define pa(X) ((X) - __PAGE_OFFSET)
@@ -673,6 +674,7 @@ ENTRY(empty_zero_page)
        .fill 4096,1,0
 ENTRY(swapper_pg_dir)
        .fill 1024,4,0
+EXPORT_SYMBOL(empty_zero_page)
 
 /*
  * This starts the data section.
index c98a559c346ed0c2b5092181130b2a3a2f7b1dcf..b4421cc191b056727f8f8c0def78a750b319a1c4 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/percpu.h>
 #include <asm/nops.h>
 #include "../entry/calling.h"
+#include <asm/export.h>
 
 #ifdef CONFIG_PARAVIRT
 #include <asm/asm-offsets.h>
@@ -486,10 +487,12 @@ early_gdt_descr_base:
 ENTRY(phys_base)
        /* This must match the first entry in level2_kernel_pgt */
        .quad   0x0000000000000000
+EXPORT_SYMBOL(phys_base)
 
 #include "../../x86/xen/xen-head.S"
        
        __PAGE_ALIGNED_BSS
 NEXT_PAGE(empty_zero_page)
        .skip PAGE_SIZE
+EXPORT_SYMBOL(empty_zero_page)
 
diff --git a/arch/x86/kernel/i386_ksyms_32.c b/arch/x86/kernel/i386_ksyms_32.c
deleted file mode 100644 (file)
index 1f9b878..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <linux/export.h>
-#include <linux/spinlock_types.h>
-
-#include <asm/checksum.h>
-#include <asm/pgtable.h>
-#include <asm/desc.h>
-#include <asm/ftrace.h>
-
-#ifdef CONFIG_FUNCTION_TRACER
-/* mcount is defined in assembly */
-EXPORT_SYMBOL(mcount);
-#endif
-
-/*
- * Note, this is a prototype to get at the symbol for
- * the export, but dont use it from C code, it is used
- * by assembly code and is not using C calling convention!
- */
-#ifndef CONFIG_X86_CMPXCHG64
-extern void cmpxchg8b_emu(void);
-EXPORT_SYMBOL(cmpxchg8b_emu);
-#endif
-
-/* Networking helper routines. */
-EXPORT_SYMBOL(csum_partial_copy_generic);
-
-EXPORT_SYMBOL(__get_user_1);
-EXPORT_SYMBOL(__get_user_2);
-EXPORT_SYMBOL(__get_user_4);
-EXPORT_SYMBOL(__get_user_8);
-
-EXPORT_SYMBOL(__put_user_1);
-EXPORT_SYMBOL(__put_user_2);
-EXPORT_SYMBOL(__put_user_4);
-EXPORT_SYMBOL(__put_user_8);
-
-EXPORT_SYMBOL(strstr);
-
-EXPORT_SYMBOL(csum_partial);
-EXPORT_SYMBOL(empty_zero_page);
-
-#ifdef CONFIG_PREEMPT
-EXPORT_SYMBOL(___preempt_schedule);
-EXPORT_SYMBOL(___preempt_schedule_notrace);
-#endif
-
-EXPORT_SYMBOL(__sw_hweight32);
index 28cee019209ce7f7fd9ec160e4409fecf000819f..d9d8d16b69db89df63b5ff33a4e597fcf4e5eba6 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/kallsyms.h>
 #include <linux/ftrace.h>
 #include <linux/frame.h>
+#include <linux/kasan.h>
 
 #include <asm/text-patching.h>
 #include <asm/cacheflush.h>
@@ -1057,9 +1058,10 @@ int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
         * tailcall optimization. So, to be absolutely safe
         * we also save and restore enough stack bytes to cover
         * the argument area.
+        * Use __memcpy() to avoid KASAN stack out-of-bounds reports as we copy
+        * raw stack chunk with redzones:
         */
-       memcpy(kcb->jprobes_stack, (kprobe_opcode_t *)addr,
-              MIN_STACK_SIZE(addr));
+       __memcpy(kcb->jprobes_stack, (kprobe_opcode_t *)addr, MIN_STACK_SIZE(addr));
        regs->flags &= ~X86_EFLAGS_IF;
        trace_hardirqs_off();
        regs->ip = (unsigned long)(jp->entry);
@@ -1080,6 +1082,9 @@ void jprobe_return(void)
 {
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
 
+       /* Unpoison stack redzones in the frames we are going to jump over. */
+       kasan_unpoison_stack_above_sp_to(kcb->jprobe_saved_sp);
+
        asm volatile (
 #ifdef CONFIG_X86_64
                        "       xchg   %%rbx,%%rsp      \n"
@@ -1118,7 +1123,7 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
                /* It's OK to start function graph tracing again */
                unpause_graph_tracing();
                *regs = kcb->jprobe_saved_regs;
-               memcpy(saved_sp, kcb->jprobes_stack, MIN_STACK_SIZE(saved_sp));
+               __memcpy(saved_sp, kcb->jprobes_stack, MIN_STACK_SIZE(saved_sp));
                preempt_enable_no_resched();
                return 1;
        }
index 61924222a9e1ceb4e582b961a0568a1301dc4b6b..efe73aacf966eb6f54e6c252f4e672a425ba7350 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/linkage.h>
 #include <asm/ptrace.h>
 #include <asm/ftrace.h>
+#include <asm/export.h>
 
 
        .code64
@@ -294,6 +295,7 @@ trace:
        jmp fgraph_trace
 END(function_hook)
 #endif /* CONFIG_DYNAMIC_FTRACE */
+EXPORT_SYMBOL(function_hook)
 #endif /* CONFIG_FUNCTION_TRACER */
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
index 68f8cc222f255aa1cf5266e2d84d7ceeb2417977..c00cb64bc0a12e5f6f36c37215b05a3afd178db9 100644 (file)
@@ -261,8 +261,10 @@ static inline void __smp_reschedule_interrupt(void)
 
 __visible void smp_reschedule_interrupt(struct pt_regs *regs)
 {
+       irq_enter();
        ack_APIC_irq();
        __smp_reschedule_interrupt();
+       irq_exit();
        /*
         * KVM uses this interrupt to force a cpu out of guest mode
         */
index c9a073866ca7b1f4c6efa5b9db110c591083b3af..a23ce84a3f6ccfefe36a0d3070880aa45d2bc0f5 100644 (file)
@@ -57,7 +57,8 @@ static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs)
        unsigned char opcode[15];
        unsigned long addr = convert_ip_to_linear(child, regs);
 
-       copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0);
+       copied = access_process_vm(child, addr, opcode, sizeof(opcode),
+                       FOLL_FORCE);
        for (i = 0; i < copied; i++) {
                switch (opcode[i]) {
                /* popf and iret */
diff --git a/arch/x86/kernel/x8664_ksyms_64.c b/arch/x86/kernel/x8664_ksyms_64.c
deleted file mode 100644 (file)
index b2cee3d..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Exports for assembly files.
-   All C exports should go in the respective C files. */
-
-#include <linux/export.h>
-#include <linux/spinlock_types.h>
-#include <linux/smp.h>
-
-#include <net/checksum.h>
-
-#include <asm/processor.h>
-#include <asm/pgtable.h>
-#include <asm/uaccess.h>
-#include <asm/desc.h>
-#include <asm/ftrace.h>
-
-#ifdef CONFIG_FUNCTION_TRACER
-/* mcount and __fentry__ are defined in assembly */
-#ifdef CC_USING_FENTRY
-EXPORT_SYMBOL(__fentry__);
-#else
-EXPORT_SYMBOL(mcount);
-#endif
-#endif
-
-EXPORT_SYMBOL(__get_user_1);
-EXPORT_SYMBOL(__get_user_2);
-EXPORT_SYMBOL(__get_user_4);
-EXPORT_SYMBOL(__get_user_8);
-EXPORT_SYMBOL(__put_user_1);
-EXPORT_SYMBOL(__put_user_2);
-EXPORT_SYMBOL(__put_user_4);
-EXPORT_SYMBOL(__put_user_8);
-
-EXPORT_SYMBOL(copy_user_generic_string);
-EXPORT_SYMBOL(copy_user_generic_unrolled);
-EXPORT_SYMBOL(copy_user_enhanced_fast_string);
-EXPORT_SYMBOL(__copy_user_nocache);
-EXPORT_SYMBOL(_copy_from_user);
-EXPORT_SYMBOL(_copy_to_user);
-
-EXPORT_SYMBOL_GPL(memcpy_mcsafe_unrolled);
-
-EXPORT_SYMBOL(copy_page);
-EXPORT_SYMBOL(clear_page);
-
-EXPORT_SYMBOL(csum_partial);
-
-EXPORT_SYMBOL(__sw_hweight32);
-EXPORT_SYMBOL(__sw_hweight64);
-
-/*
- * Export string functions. We normally rely on gcc builtin for most of these,
- * but gcc sometimes decides not to inline them.
- */
-#undef memcpy
-#undef memset
-#undef memmove
-
-extern void *__memset(void *, int, __kernel_size_t);
-extern void *__memcpy(void *, const void *, __kernel_size_t);
-extern void *__memmove(void *, const void *, __kernel_size_t);
-extern void *memset(void *, int, __kernel_size_t);
-extern void *memcpy(void *, const void *, __kernel_size_t);
-extern void *memmove(void *, const void *, __kernel_size_t);
-
-EXPORT_SYMBOL(__memset);
-EXPORT_SYMBOL(__memcpy);
-EXPORT_SYMBOL(__memmove);
-
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memmove);
-
-#ifndef CONFIG_DEBUG_VIRTUAL
-EXPORT_SYMBOL(phys_base);
-#endif
-EXPORT_SYMBOL(empty_zero_page);
-#ifndef CONFIG_PARAVIRT
-EXPORT_SYMBOL(native_load_gs_index);
-#endif
-
-#ifdef CONFIG_PREEMPT
-EXPORT_SYMBOL(___preempt_schedule);
-EXPORT_SYMBOL(___preempt_schedule_notrace);
-#endif
index c1e6232098531f0d8ce14d492215041994443b81..4d34bb548b41ebdddc20fcf464aad4cb44c6a763 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/linkage.h>
 #include <asm/errno.h>
 #include <asm/asm.h>
+#include <asm/export.h>
                                
 /*
  * computes a partial checksum, e.g. for TCP/UDP fragments
@@ -251,6 +252,7 @@ ENTRY(csum_partial)
 ENDPROC(csum_partial)
                                
 #endif
+EXPORT_SYMBOL(csum_partial)
 
 /*
 unsigned int csum_partial_copy_generic (const char *src, char *dst,
@@ -490,3 +492,4 @@ ENDPROC(csum_partial_copy_generic)
 #undef ROUND1          
                
 #endif
+EXPORT_SYMBOL(csum_partial_copy_generic)
index 65be7cfaf947228d454f2324541e5f5227d85183..5e2af3a88cf5e47e2505926938d3f239d55f2ff3 100644 (file)
@@ -1,6 +1,7 @@
 #include <linux/linkage.h>
 #include <asm/cpufeatures.h>
 #include <asm/alternative-asm.h>
+#include <asm/export.h>
 
 /*
  * Most CPUs support enhanced REP MOVSB/STOSB instructions. It is
@@ -23,6 +24,7 @@ ENTRY(clear_page)
        rep stosq
        ret
 ENDPROC(clear_page)
+EXPORT_SYMBOL(clear_page)
 
 ENTRY(clear_page_orig)
 
index ad53497784904b2c1f420fd41576c9ffeea8ce0b..03a186fc06eab6217a4d461174409ac1cc9366ee 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 .text
 
@@ -48,3 +49,4 @@ ENTRY(cmpxchg8b_emu)
        ret
 
 ENDPROC(cmpxchg8b_emu)
+EXPORT_SYMBOL(cmpxchg8b_emu)
index 24ef1c2104d422c35a9ce783934306a0dd9215f1..e8508156c99d5e0a8f3aa8a6dc2ad659d59c021e 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/linkage.h>
 #include <asm/cpufeatures.h>
 #include <asm/alternative-asm.h>
+#include <asm/export.h>
 
 /*
  * Some CPUs run faster using the string copy instructions (sane microcode).
@@ -17,6 +18,7 @@ ENTRY(copy_page)
        rep     movsq
        ret
 ENDPROC(copy_page)
+EXPORT_SYMBOL(copy_page)
 
 ENTRY(copy_page_regs)
        subq    $2*8,   %rsp
index bf603ebbfd8e26eb81f915f52103a2e9aa691cd9..d376e4b48f881b89170802ab7b6aa072c458ed8f 100644 (file)
@@ -14,6 +14,7 @@
 #include <asm/alternative-asm.h>
 #include <asm/asm.h>
 #include <asm/smap.h>
+#include <asm/export.h>
 
 /* Standard copy_to_user with segment limit checking */
 ENTRY(_copy_to_user)
@@ -29,6 +30,7 @@ ENTRY(_copy_to_user)
                      "jmp copy_user_enhanced_fast_string",     \
                      X86_FEATURE_ERMS
 ENDPROC(_copy_to_user)
+EXPORT_SYMBOL(_copy_to_user)
 
 /* Standard copy_from_user with segment limit checking */
 ENTRY(_copy_from_user)
@@ -44,6 +46,8 @@ ENTRY(_copy_from_user)
                      "jmp copy_user_enhanced_fast_string",     \
                      X86_FEATURE_ERMS
 ENDPROC(_copy_from_user)
+EXPORT_SYMBOL(_copy_from_user)
+
 
        .section .fixup,"ax"
        /* must zero dest */
@@ -155,6 +159,7 @@ ENTRY(copy_user_generic_unrolled)
        _ASM_EXTABLE(21b,50b)
        _ASM_EXTABLE(22b,50b)
 ENDPROC(copy_user_generic_unrolled)
+EXPORT_SYMBOL(copy_user_generic_unrolled)
 
 /* Some CPUs run faster using the string copy instructions.
  * This is also a lot simpler. Use them when possible.
@@ -200,6 +205,7 @@ ENTRY(copy_user_generic_string)
        _ASM_EXTABLE(1b,11b)
        _ASM_EXTABLE(3b,12b)
 ENDPROC(copy_user_generic_string)
+EXPORT_SYMBOL(copy_user_generic_string)
 
 /*
  * Some CPUs are adding enhanced REP MOVSB/STOSB instructions.
@@ -229,6 +235,7 @@ ENTRY(copy_user_enhanced_fast_string)
 
        _ASM_EXTABLE(1b,12b)
 ENDPROC(copy_user_enhanced_fast_string)
+EXPORT_SYMBOL(copy_user_enhanced_fast_string)
 
 /*
  * copy_user_nocache - Uncached memory copy with exception handling
@@ -379,3 +386,4 @@ ENTRY(__copy_user_nocache)
        _ASM_EXTABLE(40b,.L_fixup_1b_copy)
        _ASM_EXTABLE(41b,.L_fixup_1b_copy)
 ENDPROC(__copy_user_nocache)
+EXPORT_SYMBOL(__copy_user_nocache)
index 9a7fe6a70491e20c7c648d97d04a2009ac0460b6..378e5d5bf9b13834dec061738f356b8fc674b47e 100644 (file)
@@ -135,6 +135,7 @@ __wsum csum_partial(const void *buff, int len, __wsum sum)
        return (__force __wsum)add32_with_carry(do_csum(buff, len),
                                                (__force u32)sum);
 }
+EXPORT_SYMBOL(csum_partial);
 
 /*
  * this routine is used for miscellaneous IP-like checksums, mainly
index 0ef5128c2de8b05ce60e8beac1e43f53adcba2ba..37b62d4121481df990741fac20ee48c2e68492e7 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/thread_info.h>
 #include <asm/asm.h>
 #include <asm/smap.h>
+#include <asm/export.h>
 
        .text
 ENTRY(__get_user_1)
@@ -44,6 +45,7 @@ ENTRY(__get_user_1)
        ASM_CLAC
        ret
 ENDPROC(__get_user_1)
+EXPORT_SYMBOL(__get_user_1)
 
 ENTRY(__get_user_2)
        add $1,%_ASM_AX
@@ -57,6 +59,7 @@ ENTRY(__get_user_2)
        ASM_CLAC
        ret
 ENDPROC(__get_user_2)
+EXPORT_SYMBOL(__get_user_2)
 
 ENTRY(__get_user_4)
        add $3,%_ASM_AX
@@ -70,6 +73,7 @@ ENTRY(__get_user_4)
        ASM_CLAC
        ret
 ENDPROC(__get_user_4)
+EXPORT_SYMBOL(__get_user_4)
 
 ENTRY(__get_user_8)
 #ifdef CONFIG_X86_64
@@ -97,6 +101,7 @@ ENTRY(__get_user_8)
        ret
 #endif
 ENDPROC(__get_user_8)
+EXPORT_SYMBOL(__get_user_8)
 
 
 bad_get_user:
index 8a602a1e404a262f32fbe708e926e986636dcfca..23d893cbc2001094b0885ebf1811cf391192bbad 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 #include <asm/asm.h>
 
@@ -32,6 +33,7 @@ ENTRY(__sw_hweight32)
        __ASM_SIZE(pop,) %__ASM_REG(dx)
        ret
 ENDPROC(__sw_hweight32)
+EXPORT_SYMBOL(__sw_hweight32)
 
 ENTRY(__sw_hweight64)
 #ifdef CONFIG_X86_64
@@ -77,3 +79,4 @@ ENTRY(__sw_hweight64)
        ret
 #endif
 ENDPROC(__sw_hweight64)
+EXPORT_SYMBOL(__sw_hweight64)
index 49e6ebac7e73e33b0a03327cb65c95a29afc1c67..779782f5832476582becc24e5a0f0f5b10ea0b53 100644 (file)
@@ -4,6 +4,7 @@
 #include <asm/errno.h>
 #include <asm/cpufeatures.h>
 #include <asm/alternative-asm.h>
+#include <asm/export.h>
 
 /*
  * We build a jump to memcpy_orig by default which gets NOPped out on
@@ -40,6 +41,8 @@ ENTRY(memcpy)
        ret
 ENDPROC(memcpy)
 ENDPROC(__memcpy)
+EXPORT_SYMBOL(memcpy)
+EXPORT_SYMBOL(__memcpy)
 
 /*
  * memcpy_erms() - enhanced fast string memcpy. This is faster and
@@ -274,6 +277,7 @@ ENTRY(memcpy_mcsafe_unrolled)
        xorq %rax, %rax
        ret
 ENDPROC(memcpy_mcsafe_unrolled)
+EXPORT_SYMBOL_GPL(memcpy_mcsafe_unrolled)
 
        .section .fixup, "ax"
        /* Return -EFAULT for any failure */
index 90ce01bee00c17f173719652659edb5972ecef07..15de86cd15b05bf95d0275cce0d52188999ac5d0 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/linkage.h>
 #include <asm/cpufeatures.h>
 #include <asm/alternative-asm.h>
+#include <asm/export.h>
 
 #undef memmove
 
@@ -207,3 +208,5 @@ ENTRY(__memmove)
        retq
 ENDPROC(__memmove)
 ENDPROC(memmove)
+EXPORT_SYMBOL(__memmove)
+EXPORT_SYMBOL(memmove)
index e1229ecd2a82057cc6f0171169b6871f0e438167..55b95db30a61c08df145e2c321fb0362696a623f 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/linkage.h>
 #include <asm/cpufeatures.h>
 #include <asm/alternative-asm.h>
+#include <asm/export.h>
 
 .weak memset
 
@@ -43,6 +44,8 @@ ENTRY(__memset)
        ret
 ENDPROC(memset)
 ENDPROC(__memset)
+EXPORT_SYMBOL(memset)
+EXPORT_SYMBOL(__memset)
 
 /*
  * ISO C memset - set a memory block to a byte value. This function uses
index c891ece81e5b11a9b2337eac142c65c95286ad72..cd5d716d289783cd5c0d853b461701822db96a63 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/errno.h>
 #include <asm/asm.h>
 #include <asm/smap.h>
+#include <asm/export.h>
 
 
 /*
@@ -43,6 +44,7 @@ ENTRY(__put_user_1)
        xor %eax,%eax
        EXIT
 ENDPROC(__put_user_1)
+EXPORT_SYMBOL(__put_user_1)
 
 ENTRY(__put_user_2)
        ENTER
@@ -55,6 +57,7 @@ ENTRY(__put_user_2)
        xor %eax,%eax
        EXIT
 ENDPROC(__put_user_2)
+EXPORT_SYMBOL(__put_user_2)
 
 ENTRY(__put_user_4)
        ENTER
@@ -67,6 +70,7 @@ ENTRY(__put_user_4)
        xor %eax,%eax
        EXIT
 ENDPROC(__put_user_4)
+EXPORT_SYMBOL(__put_user_4)
 
 ENTRY(__put_user_8)
        ENTER
@@ -82,6 +86,7 @@ ENTRY(__put_user_8)
        xor %eax,%eax
        EXIT
 ENDPROC(__put_user_8)
+EXPORT_SYMBOL(__put_user_8)
 
 bad_put_user:
        movl $-EFAULT,%eax
index 8e2d55f754bff8f7f221cdb96b0c01c2352f32fa..a03b1c750bfed32e061b93fcfa45c81f9c9ecdad 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/string.h>
+#include <linux/export.h>
 
 char *strstr(const char *cs, const char *ct)
 {
@@ -28,4 +29,4 @@ __asm__ __volatile__(
        : "dx", "di");
 return __res;
 }
-
+EXPORT_SYMBOL(strstr);
index b8b6a60b32cf47837070518bd44d4c4742910697..0d4fb3ebbbac9872aaaf26514211ae543a253299 100644 (file)
@@ -435,7 +435,7 @@ slow_irqon:
 
                ret = get_user_pages_unlocked(start,
                                              (end - start) >> PAGE_SHIFT,
-                                             write, 0, pages);
+                                             pages, write ? FOLL_WRITE : 0);
 
                /* Have to be a bit careful with return values */
                if (nr > 0) {
index 80476878eb4ca5c8ad56340bb52b2423995a7867..e4f800999b32dc94d5ba1a1283591649a818fbb7 100644 (file)
@@ -544,10 +544,9 @@ static int mpx_resolve_fault(long __user *addr, int write)
 {
        long gup_ret;
        int nr_pages = 1;
-       int force = 0;
 
-       gup_ret = get_user_pages((unsigned long)addr, nr_pages, write,
-                       force, NULL, NULL);
+       gup_ret = get_user_pages((unsigned long)addr, nr_pages,
+                       write ? FOLL_WRITE : 0, NULL, NULL);
        /*
         * get_user_pages() returns number of pages gotten.
         * 0 means we failed to fault in and get anything,
index 3ee2bb6b440bd3296c4f616c0311aefe07c2d5ce..e7e7055a86589dea6d0f9ca043365db4ab8b1b9c 100644 (file)
@@ -8,7 +8,7 @@ else
        BITS := 64
 endif
 
-obj-y = bug.o bugs_$(BITS).o delay.o fault.o ksyms.o ldt.o \
+obj-y = bug.o bugs_$(BITS).o delay.o fault.o ldt.o \
        ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
        stub_$(BITS).o stub_segv.o \
        sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \
index fa4b8b9841ff7071a993e61d2b55ca3bc8b35575..b9933eb9274aeae84159a70c3e5e82a506536826 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <asm/errno.h>
 #include <asm/asm.h>
+#include <asm/export.h>
                                
 /*
  * computes a partial checksum, e.g. for TCP/UDP fragments
@@ -214,3 +215,4 @@ csum_partial:
        ret
                                
 #endif
+       EXPORT_SYMBOL(csum_partial)
diff --git a/arch/x86/um/ksyms.c b/arch/x86/um/ksyms.c
deleted file mode 100644 (file)
index 2e8f43e..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <linux/module.h>
-#include <asm/string.h>
-#include <asm/checksum.h>
-
-#ifndef CONFIG_X86_32
-/*XXX: we need them because they would be exported by x86_64 */
-#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4
-EXPORT_SYMBOL(memcpy);
-#else
-EXPORT_SYMBOL(__memcpy);
-#endif
-#endif
-EXPORT_SYMBOL(csum_partial);
index 5766ead6fdb9679907c63aa78f8becccc8d9ecff..60a5a5a85505dd25305aea03d0850613e51ee791 100644 (file)
@@ -36,7 +36,8 @@ int is_syscall(unsigned long addr)
                 * slow, but that doesn't matter, since it will be called only
                 * in case of singlestepping, if copy_from_user failed.
                 */
-               n = access_process_vm(current, addr, &instr, sizeof(instr), 0);
+               n = access_process_vm(current, addr, &instr, sizeof(instr),
+                               FOLL_FORCE);
                if (n != sizeof(instr)) {
                        printk(KERN_ERR "is_syscall : failed to read "
                               "instruction from 0x%lx\n", addr);
index 0b5c184dd5b3b80894c3db60ead6432ad20dca57..e30202b1716efb4243372d02e61f5f8269203419 100644 (file)
@@ -212,7 +212,8 @@ int is_syscall(unsigned long addr)
                 * slow, but that doesn't matter, since it will be called only
                 * in case of singlestepping, if copy_from_user failed.
                 */
-               n = access_process_vm(current, addr, &instr, sizeof(instr), 0);
+               n = access_process_vm(current, addr, &instr, sizeof(instr),
+                               FOLL_FORCE);
                if (n != sizeof(instr)) {
                        printk("is_syscall : failed to read instruction from "
                               "0x%lx\n", addr);
index dd38e5ced4a3fa7510f00c2ee7ff9dc636b2a7fc..b08ccbb9393a75800a3299f90537f6ee881b00f0 100644 (file)
@@ -1340,10 +1340,8 @@ int blkcg_policy_register(struct blkcg_policy *pol)
                        struct blkcg_policy_data *cpd;
 
                        cpd = pol->cpd_alloc_fn(GFP_KERNEL);
-                       if (!cpd) {
-                               mutex_unlock(&blkcg_pol_mutex);
+                       if (!cpd)
                                goto err_free_cpds;
-                       }
 
                        blkcg->cpd[pol->plid] = cpd;
                        cpd->blkcg = blkcg;
index 96631e6a22b9628f124eb7d0b106261d43cfb21f..06cf9807f49a3be1742a632f9be61c0232fcaf5c 100644 (file)
@@ -18,7 +18,7 @@ static DEFINE_PER_CPU(struct list_head, blk_cpu_done);
  * Softirq action handler - move entries to local list and loop over them
  * while passing them to the queue registered handler.
  */
-static void blk_done_softirq(struct softirq_action *h)
+static __latent_entropy void blk_done_softirq(struct softirq_action *h)
 {
        struct list_head *cpu_list, local_list;
 
index 8ea8211b2d589c6618d5037e40d653a3771edc55..eb76a4c10dbfb170e43680d8b3f836bec169ad01 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/slab.h>
 #include <linux/acpi.h>
 #include <asm/mwait.h>
+#include <xen/xen.h>
 
 #define ACPI_PROCESSOR_AGGREGATOR_CLASS        "acpi_pad"
 #define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator"
@@ -477,6 +478,10 @@ static struct acpi_driver acpi_pad_driver = {
 
 static int __init acpi_pad_init(void)
 {
+       /* Xen ACPI PAD is used when running as Xen Dom0. */
+       if (xen_initial_domain())
+               return -ENODEV;
+
        power_saving_mwait_init();
        if (power_saving_mwait_eax == 0)
                return -EINVAL;
index 6805310621607335a2cf14763acc48f629d3ba31..48e19d013170936ef494d0fa88d275d37f25071a 100644 (file)
@@ -526,6 +526,7 @@ static void acpi_ec_enable_event(struct acpi_ec *ec)
                acpi_ec_clear(ec);
 }
 
+#ifdef CONFIG_PM_SLEEP
 static bool acpi_ec_query_flushed(struct acpi_ec *ec)
 {
        bool flushed;
@@ -557,6 +558,7 @@ static void acpi_ec_disable_event(struct acpi_ec *ec)
        spin_unlock_irqrestore(&ec->lock, flags);
        __acpi_ec_flush_event(ec);
 }
+#endif /* CONFIG_PM_SLEEP */
 
 static bool acpi_ec_guard_event(struct acpi_ec *ec)
 {
index 384cfc3083e1d98c544918b49ec517a9399c27a3..6cf4988206f24e8292099ae7141914ced024affd 100644 (file)
@@ -129,8 +129,18 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
 
        control = obj->package.elements[1].integer.value;
        for (i = 0; i < fan->fps_count; i++) {
-               if (control == fan->fps[i].control)
+               /*
+                * When Fine Grain Control is set, return the state
+                * corresponding to maximum fan->fps[i].control
+                * value compared to the current speed. Here the
+                * fan->fps[] is sorted array with increasing speed.
+                */
+               if (fan->fif.fine_grain_ctrl && control < fan->fps[i].control) {
+                       i = (i > 0) ? i - 1 : 0;
                        break;
+               } else if (control == fan->fps[i].control) {
+                       break;
+               }
        }
        if (i == fan->fps_count) {
                dev_dbg(&device->dev, "Invalid control value returned\n");
index 4305ee9db4b2d880faaf6314cde028546b5207a6..416953a4251094ce2066d724c1d88583b8e89705 100644 (file)
@@ -162,11 +162,18 @@ void acpi_os_vprintf(const char *fmt, va_list args)
        if (acpi_in_debugger) {
                kdb_printf("%s", buffer);
        } else {
-               printk(KERN_CONT "%s", buffer);
+               if (printk_get_level(buffer))
+                       printk("%s", buffer);
+               else
+                       printk(KERN_CONT "%s", buffer);
        }
 #else
-       if (acpi_debugger_write_log(buffer) < 0)
-               printk(KERN_CONT "%s", buffer);
+       if (acpi_debugger_write_log(buffer) < 0) {
+               if (printk_get_level(buffer))
+                       printk("%s", buffer);
+               else
+                       printk(KERN_CONT "%s", buffer);
+       }
 #endif
 }
 
index f2fd3fee588a8233a80bdc5da3549fea5703b9f8..03f5ec11ab3197de66ced4e7f09d39dbab6b8232 100644 (file)
@@ -468,10 +468,11 @@ static int acpi_data_get_property_array(struct acpi_device_data *data,
 }
 
 /**
- * acpi_data_get_property_reference - returns handle to the referenced object
- * @data: ACPI device data object containing the property
+ * __acpi_node_get_property_reference - returns handle to the referenced object
+ * @fwnode: Firmware node to get the property from
  * @propname: Name of the property
  * @index: Index of the reference to return
+ * @num_args: Maximum number of arguments after each reference
  * @args: Location to store the returned reference with optional arguments
  *
  * Find property with @name, verifify that it is a package containing at least
@@ -482,17 +483,40 @@ static int acpi_data_get_property_array(struct acpi_device_data *data,
  * If there's more than one reference in the property value package, @index is
  * used to select the one to return.
  *
+ * It is possible to leave holes in the property value set like in the
+ * example below:
+ *
+ * Package () {
+ *     "cs-gpios",
+ *     Package () {
+ *        ^GPIO, 19, 0, 0,
+ *        ^GPIO, 20, 0, 0,
+ *        0,
+ *        ^GPIO, 21, 0, 0,
+ *     }
+ * }
+ *
+ * Calling this function with index %2 return %-ENOENT and with index %3
+ * returns the last entry. If the property does not contain any more values
+ * %-ENODATA is returned. The NULL entry must be single integer and
+ * preferably contain value %0.
+ *
  * Return: %0 on success, negative error code on failure.
  */
-static int acpi_data_get_property_reference(struct acpi_device_data *data,
-                                           const char *propname, size_t index,
-                                           struct acpi_reference_args *args)
+int __acpi_node_get_property_reference(struct fwnode_handle *fwnode,
+       const char *propname, size_t index, size_t num_args,
+       struct acpi_reference_args *args)
 {
        const union acpi_object *element, *end;
        const union acpi_object *obj;
+       struct acpi_device_data *data;
        struct acpi_device *device;
        int ret, idx = 0;
 
+       data = acpi_device_data_of_node(fwnode);
+       if (!data)
+               return -EINVAL;
+
        ret = acpi_data_get_property(data, propname, ACPI_TYPE_ANY, &obj);
        if (ret)
                return ret;
@@ -532,59 +556,54 @@ static int acpi_data_get_property_reference(struct acpi_device_data *data,
        while (element < end) {
                u32 nargs, i;
 
-               if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
-                       return -EPROTO;
-
-               ret = acpi_bus_get_device(element->reference.handle, &device);
-               if (ret)
-                       return -ENODEV;
-
-               element++;
-               nargs = 0;
-
-               /* assume following integer elements are all args */
-               for (i = 0; element + i < end; i++) {
-                       int type = element[i].type;
+               if (element->type == ACPI_TYPE_LOCAL_REFERENCE) {
+                       ret = acpi_bus_get_device(element->reference.handle,
+                                                 &device);
+                       if (ret)
+                               return -ENODEV;
+
+                       nargs = 0;
+                       element++;
+
+                       /* assume following integer elements are all args */
+                       for (i = 0; element + i < end && i < num_args; i++) {
+                               int type = element[i].type;
+
+                               if (type == ACPI_TYPE_INTEGER)
+                                       nargs++;
+                               else if (type == ACPI_TYPE_LOCAL_REFERENCE)
+                                       break;
+                               else
+                                       return -EPROTO;
+                       }
 
-                       if (type == ACPI_TYPE_INTEGER)
-                               nargs++;
-                       else if (type == ACPI_TYPE_LOCAL_REFERENCE)
-                               break;
-                       else
+                       if (nargs > MAX_ACPI_REFERENCE_ARGS)
                                return -EPROTO;
-               }
 
-               if (idx++ == index) {
-                       args->adev = device;
-                       args->nargs = nargs;
-                       for (i = 0; i < nargs; i++)
-                               args->args[i] = element[i].integer.value;
+                       if (idx == index) {
+                               args->adev = device;
+                               args->nargs = nargs;
+                               for (i = 0; i < nargs; i++)
+                                       args->args[i] = element[i].integer.value;
 
-                       return 0;
+                               return 0;
+                       }
+
+                       element += nargs;
+               } else if (element->type == ACPI_TYPE_INTEGER) {
+                       if (idx == index)
+                               return -ENOENT;
+                       element++;
+               } else {
+                       return -EPROTO;
                }
 
-               element += nargs;
+               idx++;
        }
 
-       return -EPROTO;
-}
-
-/**
- * acpi_node_get_property_reference - get a handle to the referenced object.
- * @fwnode: Firmware node to get the property from.
- * @propname: Name of the property.
- * @index: Index of the reference to return.
- * @args: Location to store the returned reference with optional arguments.
- */
-int acpi_node_get_property_reference(struct fwnode_handle *fwnode,
-                                    const char *name, size_t index,
-                                    struct acpi_reference_args *args)
-{
-       struct acpi_device_data *data = acpi_device_data_of_node(fwnode);
-
-       return data ? acpi_data_get_property_reference(data, name, index, args) : -EINVAL;
+       return -ENODATA;
 }
-EXPORT_SYMBOL_GPL(acpi_node_get_property_reference);
+EXPORT_SYMBOL_GPL(__acpi_node_get_property_reference);
 
 static int acpi_data_prop_read_single(struct acpi_device_data *data,
                                      const char *propname,
index f4ebe39539afb1cb2d986dd77c3371eeb3fcaf00..35e8fbca10ad5eb847b82f028600ebfb738203a5 100644 (file)
@@ -520,7 +520,8 @@ static void acpi_thermal_check(void *data)
        if (!tz->tz_enabled)
                return;
 
-       thermal_zone_device_update(tz->thermal_zone);
+       thermal_zone_device_update(tz->thermal_zone,
+                                  THERMAL_EVENT_UNSPECIFIED);
 }
 
 /* sys I/F for generic thermal sysfs support */
index 90eabaf812150dbfcff32e173f4c48f2cfcd801c..ba5f11cebee2a1ce6528116cdc9af25b7a25af44 100644 (file)
@@ -1400,142 +1400,56 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance)
 }
 #endif
 
-/*
- * ahci_init_msix() - optionally enable per-port MSI-X otherwise defer
- * to single msi.
- */
-static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
-                         struct ahci_host_priv *hpriv, unsigned long flags)
+static int ahci_get_irq_vector(struct ata_host *host, int port)
 {
-       int nvec, i, rc;
-
-       /* Do not init MSI-X if MSI is disabled for the device */
-       if (hpriv->flags & AHCI_HFLAG_NO_MSI)
-               return -ENODEV;
-
-       nvec = pci_msix_vec_count(pdev);
-       if (nvec < 0)
-               return nvec;
-
-       /*
-        * Proper MSI-X implementations will have a vector per-port.
-        * Barring that, we prefer single-MSI over single-MSIX.  If this
-        * check fails (not enough MSI-X vectors for all ports) we will
-        * be called again with the flag clear iff ahci_init_msi()
-        * fails.
-        */
-       if (flags & AHCI_HFLAG_MULTI_MSIX) {
-               if (nvec < n_ports)
-                       return -ENODEV;
-               nvec = n_ports;
-       } else if (nvec) {
-               nvec = 1;
-       } else {
-               /*
-                * Emit dev_err() since this was the non-legacy irq
-                * method of last resort.
-                */
-               rc = -ENODEV;
-               goto fail;
-       }
-
-       for (i = 0; i < nvec; i++)
-               hpriv->msix[i].entry = i;
-       rc = pci_enable_msix_exact(pdev, hpriv->msix, nvec);
-       if (rc < 0)
-               goto fail;
-
-       if (nvec > 1)
-               hpriv->flags |= AHCI_HFLAG_MULTI_MSIX;
-       hpriv->irq = hpriv->msix[0].vector; /* for single msi-x */
-
-       return nvec;
-fail:
-       dev_err(&pdev->dev,
-               "failed to enable MSI-X with error %d, # of vectors: %d\n",
-               rc, nvec);
-
-       return rc;
+       return pci_irq_vector(to_pci_dev(host->dev), port);
 }
 
 static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports,
                        struct ahci_host_priv *hpriv)
 {
-       int rc, nvec;
+       int nvec;
 
        if (hpriv->flags & AHCI_HFLAG_NO_MSI)
                return -ENODEV;
 
-       nvec = pci_msi_vec_count(pdev);
-       if (nvec < 0)
-               return nvec;
-
        /*
         * If number of MSIs is less than number of ports then Sharing Last
         * Message mode could be enforced. In this case assume that advantage
         * of multipe MSIs is negated and use single MSI mode instead.
         */
-       if (nvec < n_ports)
-               goto single_msi;
-
-       rc = pci_enable_msi_exact(pdev, nvec);
-       if (rc == -ENOSPC)
-               goto single_msi;
-       if (rc < 0)
-               return rc;
+       nvec = pci_alloc_irq_vectors(pdev, n_ports, INT_MAX,
+                       PCI_IRQ_MSIX | PCI_IRQ_MSI);
+       if (nvec > 0) {
+               if (!(readl(hpriv->mmio + HOST_CTL) & HOST_MRSM)) {
+                       hpriv->get_irq_vector = ahci_get_irq_vector;
+                       hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
+                       return nvec;
+               }
 
-       /* fallback to single MSI mode if the controller enforced MRSM mode */
-       if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) {
-               pci_disable_msi(pdev);
+               /*
+                * Fallback to single MSI mode if the controller enforced MRSM
+                * mode.
+                */
                printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n");
-               goto single_msi;
+               pci_free_irq_vectors(pdev);
        }
 
-       if (nvec > 1)
-               hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
-
-       goto out;
-
-single_msi:
-       nvec = 1;
-
-       rc = pci_enable_msi(pdev);
-       if (rc < 0)
-               return rc;
-out:
-       hpriv->irq = pdev->irq;
-
-       return nvec;
-}
-
-static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
-                               struct ahci_host_priv *hpriv)
-{
-       int nvec;
-
        /*
-        * Try to enable per-port MSI-X.  If the host is not capable
-        * fall back to single MSI before finally attempting single
-        * MSI-X.
+        * -ENOSPC indicated we don't have enough vectors.  Don't bother trying
+        * a single vectors for any other error:
         */
-       nvec = ahci_init_msix(pdev, n_ports, hpriv, AHCI_HFLAG_MULTI_MSIX);
-       if (nvec >= 0)
+       if (nvec < 0 && nvec != -ENOSPC)
                return nvec;
 
-       nvec = ahci_init_msi(pdev, n_ports, hpriv);
-       if (nvec >= 0)
-               return nvec;
-
-       /* try single-msix */
-       nvec = ahci_init_msix(pdev, n_ports, hpriv, 0);
-       if (nvec >= 0)
+       /*
+        * If the host is not capable of supporting per-port vectors, fall
+        * back to single MSI before finally attempting single MSI-X.
+        */
+       nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
+       if (nvec == 1)
                return nvec;
-
-       /* legacy intx interrupts */
-       pci_intx(pdev, 1);
-       hpriv->irq = pdev->irq;
-
-       return 0;
+       return pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
 }
 
 static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -1698,11 +1612,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (!host)
                return -ENOMEM;
        host->private_data = hpriv;
-       hpriv->msix = devm_kzalloc(&pdev->dev,
-                       sizeof(struct msix_entry) * n_ports, GFP_KERNEL);
-       if (!hpriv->msix)
-               return -ENOMEM;
-       ahci_init_interrupts(pdev, n_ports, hpriv);
+
+       if (ahci_init_msi(pdev, n_ports, hpriv) < 0) {
+               /* legacy intx interrupts */
+               pci_intx(pdev, 1);
+       }
+       hpriv->irq = pdev->irq;
 
        if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
                host->flags |= ATA_HOST_PARALLEL_SCAN;
index 70b06bcfb7e3535b47e02a913da680f67595888e..0cc08f892feaeeacf1915df8588d236e32e8960d 100644 (file)
@@ -242,12 +242,10 @@ enum {
        AHCI_HFLAG_NO_FBS               = (1 << 18), /* no FBS */
 
 #ifdef CONFIG_PCI_MSI
-       AHCI_HFLAG_MULTI_MSI            = (1 << 20), /* multiple PCI MSIs */
-       AHCI_HFLAG_MULTI_MSIX           = (1 << 21), /* per-port MSI-X */
+       AHCI_HFLAG_MULTI_MSI            = (1 << 20), /* per-port MSI(-X) */
 #else
        /* compile out MSI infrastructure */
        AHCI_HFLAG_MULTI_MSI            = 0,
-       AHCI_HFLAG_MULTI_MSIX           = 0,
 #endif
        AHCI_HFLAG_WAKE_BEFORE_STOP     = (1 << 22), /* wake before DMA stop */
 
@@ -351,7 +349,6 @@ struct ahci_host_priv {
         * the PHY position in this array.
         */
        struct phy              **phys;
-       struct msix_entry       *msix;          /* Optional MSI-X support */
        unsigned                nports;         /* Number of ports */
        void                    *plat_data;     /* Other platform data */
        unsigned int            irq;            /* interrupt line */
@@ -362,22 +359,11 @@ struct ahci_host_priv {
         */
        void                    (*start_engine)(struct ata_port *ap);
        irqreturn_t             (*irq_handler)(int irq, void *dev_instance);
-};
 
-#ifdef CONFIG_PCI_MSI
-static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port)
-{
-       if (hpriv->flags & AHCI_HFLAG_MULTI_MSIX)
-               return hpriv->msix[port].vector;
-       else
-               return hpriv->irq + port;
-}
-#else
-static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port)
-{
-       return hpriv->irq;
-}
-#endif
+       /* only required for per-port MSI(-X) support */
+       int                     (*get_irq_vector)(struct ata_host *host,
+                                                 int port);
+};
 
 extern int ahci_ignore_sss;
 
index 7bdee9bd8786638049c51d952520f2fe298df694..1eba8dff875eb6d45566779baab68aef4ba412e2 100644 (file)
 #define PORT_PHY3      0xB0
 #define PORT_PHY4      0xB4
 #define PORT_PHY5      0xB8
+#define PORT_AXICC     0xBC
 #define PORT_TRANS     0xC8
 
 /* port register default value */
 #define AHCI_PORT_PHY_1_CFG    0xa003fffe
 #define AHCI_PORT_TRANS_CFG    0x08000029
+#define AHCI_PORT_AXICC_CFG    0x3fffffff
 
 /* for ls1021a */
 #define LS1021A_PORT_PHY2      0x28183414
 #define LS1021A_PORT_PHY3      0x0e080e06
 #define LS1021A_PORT_PHY4      0x064a080b
 #define LS1021A_PORT_PHY5      0x2aa86470
+#define LS1021A_AXICC_ADDR     0xC0
 
 #define SATA_ECC_DISABLE       0x00020000
 
-/* for ls1043a */
-#define LS1043A_PORT_PHY2      0x28184d1f
-#define LS1043A_PORT_PHY3      0x0e081509
-
 enum ahci_qoriq_type {
        AHCI_LS1021A,
        AHCI_LS1043A,
@@ -137,7 +136,7 @@ static struct ata_port_operations ahci_qoriq_ops = {
        .hardreset      = ahci_qoriq_hardreset,
 };
 
-static struct ata_port_info ahci_qoriq_port_info = {
+static const struct ata_port_info ahci_qoriq_port_info = {
        .flags          = AHCI_FLAG_COMMON | ATA_FLAG_NCQ,
        .pio_mask       = ATA_PIO4,
        .udma_mask      = ATA_UDMA6,
@@ -162,18 +161,19 @@ static int ahci_qoriq_phy_init(struct ahci_host_priv *hpriv)
                writel(LS1021A_PORT_PHY4, reg_base + PORT_PHY4);
                writel(LS1021A_PORT_PHY5, reg_base + PORT_PHY5);
                writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
+               writel(AHCI_PORT_AXICC_CFG, reg_base + LS1021A_AXICC_ADDR);
                break;
 
        case AHCI_LS1043A:
                writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
-               writel(LS1043A_PORT_PHY2, reg_base + PORT_PHY2);
-               writel(LS1043A_PORT_PHY3, reg_base + PORT_PHY3);
                writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
+               writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
                break;
 
        case AHCI_LS2080A:
                writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
                writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
+               writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
                break;
        }
 
@@ -221,12 +221,6 @@ static int ahci_qoriq_probe(struct platform_device *pdev)
        if (rc)
                goto disable_resources;
 
-       /* Workaround for ls2080a */
-       if (qoriq_priv->type == AHCI_LS2080A) {
-               hpriv->flags |= AHCI_HFLAG_NO_NCQ;
-               ahci_qoriq_port_info.flags &= ~ATA_FLAG_NCQ;
-       }
-
        rc = ahci_platform_init_host(pdev, hpriv, &ahci_qoriq_port_info,
                                     &ahci_qoriq_sht);
        if (rc)
index 8ff428fe8e0fa00659218f58f041cf948ae8327c..bc345f24955531fba69e36e1bc0233da3bb6fc88 100644 (file)
@@ -147,6 +147,7 @@ static struct scsi_host_template ahci_platform_sht = {
 
 static int st_ahci_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct st_ahci_drv_data *drv_data;
        struct ahci_host_priv *hpriv;
        int err;
@@ -170,6 +171,9 @@ static int st_ahci_probe(struct platform_device *pdev)
 
        st_ahci_configure_oob(hpriv->mmio);
 
+       of_property_read_u32(dev->of_node,
+                            "ports-implemented", &hpriv->force_port_map);
+
        err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info,
                                      &ahci_platform_sht);
        if (err) {
index dcf2c724fd066c33cf74a3f502d2ae6ba3d276bc..0d028ead99e85b632d1802af0f438287bc5a7c54 100644 (file)
@@ -2520,7 +2520,7 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host,
         */
        for (i = 0; i < host->n_ports; i++) {
                struct ahci_port_priv *pp = host->ports[i]->private_data;
-               int irq = ahci_irq_vector(hpriv, i);
+               int irq = hpriv->get_irq_vector(host, i);
 
                /* Do not receive interrupts sent by dummy ports */
                if (!pp) {
@@ -2556,10 +2556,15 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)
        int irq = hpriv->irq;
        int rc;
 
-       if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX)) {
+       if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) {
                if (hpriv->irq_handler)
                        dev_warn(host->dev,
                                 "both AHCI_HFLAG_MULTI_MSI flag set and custom irq handler implemented\n");
+               if (!hpriv->get_irq_vector) {
+                       dev_err(host->dev,
+                               "AHCI_HFLAG_MULTI_MSI requires ->get_irq_vector!\n");
+                       return -EIO;
+               }
 
                rc = ahci_host_activate_multi_irqs(host, sht);
        } else {
index e207b33e4ce9d602ca5d72144a7332c91f2515c9..9cceb4a875a58caa19fdd809ad82181f22676fc2 100644 (file)
@@ -1159,8 +1159,6 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev)
 {
        sdev->use_10_for_rw = 1;
        sdev->use_10_for_ms = 1;
-       sdev->no_report_opcodes = 1;
-       sdev->no_write_same = 1;
 
        /* Schedule policy is determined by ->qc_defer() callback and
         * it needs to see every deferred qc.  Set dev_blocked to 1 to
@@ -3282,18 +3280,125 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
        return 1;
 }
 
+/**
+ * ata_format_dsm_trim_descr() - SATL Write Same to DSM Trim
+ * @cmd: SCSI command being translated
+ * @trmax: Maximum number of entries that will fit in sector_size bytes.
+ * @sector: Starting sector
+ * @count: Total Range of request in logical sectors
+ *
+ * Rewrite the WRITE SAME descriptor to be a DSM TRIM little-endian formatted
+ * descriptor.
+ *
+ * Upto 64 entries of the format:
+ *   63:48 Range Length
+ *   47:0  LBA
+ *
+ *  Range Length of 0 is ignored.
+ *  LBA's should be sorted order and not overlap.
+ *
+ * NOTE: this is the same format as ADD LBA(S) TO NV CACHE PINNED SET
+ *
+ * Return: Number of bytes copied into sglist.
+ */
+static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax,
+                                       u64 sector, u32 count)
+{
+       struct scsi_device *sdp = cmd->device;
+       size_t len = sdp->sector_size;
+       size_t r;
+       __le64 *buf;
+       u32 i = 0;
+       unsigned long flags;
+
+       WARN_ON(len > ATA_SCSI_RBUF_SIZE);
+
+       if (len > ATA_SCSI_RBUF_SIZE)
+               len = ATA_SCSI_RBUF_SIZE;
+
+       spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
+       buf = ((void *)ata_scsi_rbuf);
+       memset(buf, 0, len);
+       while (i < trmax) {
+               u64 entry = sector |
+                       ((u64)(count > 0xffff ? 0xffff : count) << 48);
+               buf[i++] = __cpu_to_le64(entry);
+               if (count <= 0xffff)
+                       break;
+               count -= 0xffff;
+               sector += 0xffff;
+       }
+       r = sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), buf, len);
+       spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags);
+
+       return r;
+}
+
+/**
+ * ata_format_dsm_trim_descr() - SATL Write Same to ATA SCT Write Same
+ * @cmd: SCSI command being translated
+ * @lba: Starting sector
+ * @num: Number of sectors to be zero'd.
+ *
+ * Rewrite the WRITE SAME payload to be an SCT Write Same formatted
+ * descriptor.
+ * NOTE: Writes a pattern (0's) in the foreground.
+ *
+ * Return: Number of bytes copied into sglist.
+ */
+static size_t ata_format_sct_write_same(struct scsi_cmnd *cmd, u64 lba, u64 num)
+{
+       struct scsi_device *sdp = cmd->device;
+       size_t len = sdp->sector_size;
+       size_t r;
+       u16 *buf;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
+       buf = ((void *)ata_scsi_rbuf);
+
+       put_unaligned_le16(0x0002,  &buf[0]); /* SCT_ACT_WRITE_SAME */
+       put_unaligned_le16(0x0101,  &buf[1]); /* WRITE PTRN FG */
+       put_unaligned_le64(lba,     &buf[2]);
+       put_unaligned_le64(num,     &buf[6]);
+       put_unaligned_le32(0u,      &buf[10]); /* pattern */
+
+       WARN_ON(len > ATA_SCSI_RBUF_SIZE);
+
+       if (len > ATA_SCSI_RBUF_SIZE)
+               len = ATA_SCSI_RBUF_SIZE;
+
+       r = sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), buf, len);
+       spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags);
+
+       return r;
+}
+
+/**
+ * ata_scsi_write_same_xlat() - SATL Write Same to ATA SCT Write Same
+ * @qc: Command to be translated
+ *
+ * Translate a SCSI WRITE SAME command to be either a DSM TRIM command or
+ * an SCT Write Same command.
+ * Based on WRITE SAME has the UNMAP flag
+ *   When set translate to DSM TRIM
+ *   When clear translate to SCT Write Same
+ */
 static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
 {
        struct ata_taskfile *tf = &qc->tf;
        struct scsi_cmnd *scmd = qc->scsicmd;
+       struct scsi_device *sdp = scmd->device;
+       size_t len = sdp->sector_size;
        struct ata_device *dev = qc->dev;
        const u8 *cdb = scmd->cmnd;
        u64 block;
        u32 n_block;
+       const u32 trmax = len >> 3;
        u32 size;
-       void *buf;
        u16 fp;
        u8 bp = 0xff;
+       u8 unmap = cdb[1] & 0x8;
 
        /* we may not issue DMA commands if no DMA mode is set */
        if (unlikely(!dev->dma_mode))
@@ -3305,11 +3410,26 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
        }
        scsi_16_lba_len(cdb, &block, &n_block);
 
-       /* for now we only support WRITE SAME with the unmap bit set */
-       if (unlikely(!(cdb[1] & 0x8))) {
-               fp = 1;
-               bp = 3;
-               goto invalid_fld;
+       if (unmap) {
+               /* If trim is not enabled the cmd is invalid. */
+               if ((dev->horkage & ATA_HORKAGE_NOTRIM) ||
+                   !ata_id_has_trim(dev->id)) {
+                       fp = 1;
+                       bp = 3;
+                       goto invalid_fld;
+               }
+               /* If the request is too large the cmd is invalid */
+               if (n_block > 0xffff * trmax) {
+                       fp = 2;
+                       goto invalid_fld;
+               }
+       } else {
+               /* If write same is not available the cmd is invalid */
+               if (!ata_id_sct_write_same(dev->id)) {
+                       fp = 1;
+                       bp = 3;
+                       goto invalid_fld;
+               }
        }
 
        /*
@@ -3319,32 +3439,54 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
        if (!scsi_sg_count(scmd))
                goto invalid_param_len;
 
-       buf = page_address(sg_page(scsi_sglist(scmd)));
-
-       if (n_block <= 65535 * ATA_MAX_TRIM_RNUM) {
-               size = ata_set_lba_range_entries(buf, ATA_MAX_TRIM_RNUM, block, n_block);
-       } else {
-               fp = 2;
-               goto invalid_fld;
-       }
+       /*
+        * size must match sector size in bytes
+        * For DATA SET MANAGEMENT TRIM in ACS-2 nsect (aka count)
+        * is defined as number of 512 byte blocks to be transferred.
+        */
+       if (unmap) {
+               size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block);
+               if (size != len)
+                       goto invalid_param_len;
 
-       if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) {
-               /* Newer devices support queued TRIM commands */
-               tf->protocol = ATA_PROT_NCQ;
-               tf->command = ATA_CMD_FPDMA_SEND;
-               tf->hob_nsect = ATA_SUBCMD_FPDMA_SEND_DSM & 0x1f;
-               tf->nsect = qc->tag << 3;
-               tf->hob_feature = (size / 512) >> 8;
-               tf->feature = size / 512;
+               if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) {
+                       /* Newer devices support queued TRIM commands */
+                       tf->protocol = ATA_PROT_NCQ;
+                       tf->command = ATA_CMD_FPDMA_SEND;
+                       tf->hob_nsect = ATA_SUBCMD_FPDMA_SEND_DSM & 0x1f;
+                       tf->nsect = qc->tag << 3;
+                       tf->hob_feature = (size / 512) >> 8;
+                       tf->feature = size / 512;
 
-               tf->auxiliary = 1;
+                       tf->auxiliary = 1;
+               } else {
+                       tf->protocol = ATA_PROT_DMA;
+                       tf->hob_feature = 0;
+                       tf->feature = ATA_DSM_TRIM;
+                       tf->hob_nsect = (size / 512) >> 8;
+                       tf->nsect = size / 512;
+                       tf->command = ATA_CMD_DSM;
+               }
        } else {
-               tf->protocol = ATA_PROT_DMA;
+               size = ata_format_sct_write_same(scmd, block, n_block);
+               if (size != len)
+                       goto invalid_param_len;
+
                tf->hob_feature = 0;
-               tf->feature = ATA_DSM_TRIM;
-               tf->hob_nsect = (size / 512) >> 8;
-               tf->nsect = size / 512;
-               tf->command = ATA_CMD_DSM;
+               tf->feature = 0;
+               tf->hob_nsect = 0;
+               tf->nsect = 1;
+               tf->lbah = 0;
+               tf->lbam = 0;
+               tf->lbal = ATA_CMD_STANDBYNOW1;
+               tf->hob_lbah = 0;
+               tf->hob_lbam = 0;
+               tf->hob_lbal = 0;
+               tf->device = ATA_CMD_STANDBYNOW1;
+               tf->protocol = ATA_PROT_DMA;
+               tf->command = ATA_CMD_WRITE_LOG_DMA_EXT;
+               if (unlikely(dev->flags & ATA_DFLAG_PIO))
+                       tf->command = ATA_CMD_WRITE_LOG_EXT;
        }
 
        tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 |
@@ -3367,6 +3509,76 @@ invalid_opcode:
        return 1;
 }
 
+/**
+ *     ata_scsiop_maint_in - Simulate a subset of MAINTENANCE_IN
+ *     @args: device MAINTENANCE_IN data / SCSI command of interest.
+ *     @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ *     Yields a subset to satisfy scsi_report_opcode()
+ *
+ *     LOCKING:
+ *     spin_lock_irqsave(host lock)
+ */
+static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
+{
+       struct ata_device *dev = args->dev;
+       u8 *cdb = args->cmd->cmnd;
+       u8 supported = 0;
+       unsigned int err = 0;
+
+       if (cdb[2] != 1) {
+               ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
+               err = 2;
+               goto out;
+       }
+       switch (cdb[3]) {
+       case INQUIRY:
+       case MODE_SENSE:
+       case MODE_SENSE_10:
+       case READ_CAPACITY:
+       case SERVICE_ACTION_IN_16:
+       case REPORT_LUNS:
+       case REQUEST_SENSE:
+       case SYNCHRONIZE_CACHE:
+       case REZERO_UNIT:
+       case SEEK_6:
+       case SEEK_10:
+       case TEST_UNIT_READY:
+       case SEND_DIAGNOSTIC:
+       case MAINTENANCE_IN:
+       case READ_6:
+       case READ_10:
+       case READ_16:
+       case WRITE_6:
+       case WRITE_10:
+       case WRITE_16:
+       case ATA_12:
+       case ATA_16:
+       case VERIFY:
+       case VERIFY_16:
+       case MODE_SELECT:
+       case MODE_SELECT_10:
+       case START_STOP:
+               supported = 3;
+               break;
+       case WRITE_SAME_16:
+               if (!ata_id_sct_write_same(dev->id))
+                       break;
+               /* fallthrough: if SCT ... only enable for ZBC */
+       case ZBC_IN:
+       case ZBC_OUT:
+               if (ata_id_zoned_cap(dev->id) ||
+                   dev->class == ATA_DEV_ZAC)
+                       supported = 3;
+               break;
+       default:
+               break;
+       }
+out:
+       rbuf[1] = supported; /* supported */
+       return err;
+}
+
 /**
  *     ata_scsi_report_zones_complete - convert ATA output
  *     @qc: command structure returning the data
@@ -3610,7 +3822,7 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc,
 {
        struct ata_taskfile *tf = &qc->tf;
        struct ata_device *dev = qc->dev;
-       char mpage[CACHE_MPAGE_LEN];
+       u8 mpage[CACHE_MPAGE_LEN];
        u8 wce;
        int i;
 
@@ -3666,7 +3878,7 @@ static int ata_mselect_control(struct ata_queued_cmd *qc,
                               const u8 *buf, int len, u16 *fp)
 {
        struct ata_device *dev = qc->dev;
-       char mpage[CONTROL_MPAGE_LEN];
+       u8 mpage[CONTROL_MPAGE_LEN];
        u8 d_sense;
        int i;
 
@@ -3701,8 +3913,6 @@ static int ata_mselect_control(struct ata_queued_cmd *qc,
                dev->flags |= ATA_DFLAG_D_SENSE;
        else
                dev->flags &= ~ATA_DFLAG_D_SENSE;
-       qc->scsicmd->result = SAM_STAT_GOOD;
-       qc->scsicmd->scsi_done(qc->scsicmd);
        return 0;
 }
 
@@ -3829,6 +4039,8 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
                if (ata_mselect_control(qc, p, pg_len, &fp) < 0) {
                        fp += hdr_len + bd_len;
                        goto invalid_param;
+               } else {
+                       goto skip; /* No ATA command to send */
                }
                break;
        default:                /* invalid page code */
@@ -4147,6 +4359,13 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
                        ata_scsi_invalid_field(dev, cmd, 1);
                break;
 
+       case MAINTENANCE_IN:
+               if (scsicmd[1] == MI_REPORT_SUPPORTED_OPERATION_CODES)
+                       ata_scsi_rbuf_fill(&args, ata_scsiop_maint_in);
+               else
+                       ata_scsi_invalid_field(dev, cmd, 1);
+               break;
+
        /* all other commands */
        default:
                ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x20, 0x0);
@@ -4179,7 +4398,6 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
                shost->max_lun = 1;
                shost->max_channel = 1;
                shost->max_cmd_len = 16;
-               shost->no_write_same = 1;
 
                /* Schedule policy is determined by ->qc_defer()
                 * callback and it needs to see every deferred qc.
index 9f27b14009f9e0f9b34b095b99dfeae76f5d72c4..1611e0e8d767816d86127f007f36818ab8a04434 100644 (file)
@@ -347,10 +347,8 @@ static int at91sam9_smc_fields_init(struct device *dev)
 
        field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC);
        fields.mode = devm_regmap_field_alloc(dev, smc, field);
-       if (IS_ERR(fields.mode))
-               return PTR_ERR(fields.mode);
 
-       return 0;
+       return PTR_ERR_OR_ZERO(fields.mode);
 }
 
 static int pata_at91_probe(struct platform_device *pdev)
index 27245957eee3cd906f546d67853d2ebd6ce54d30..475a006694273bed1262dd457e8a9f683ba0367e 100644 (file)
@@ -152,8 +152,7 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev)
                div = 8;
        T = (int)((1000000000000LL * div) / octeon_get_io_clock_rate());
 
-       if (ata_timing_compute(dev, dev->pio_mode, &timing, T, T))
-               BUG();
+       BUG_ON(ata_timing_compute(dev, dev->pio_mode, &timing, T, T));
 
        t1 = timing.setup;
        if (t1)
index 745489a1c86ab2c11701b124f8998126608c398a..efc48bf89d5182a6b4b4150b522fabc2cc1844ee 100644 (file)
@@ -1727,15 +1727,13 @@ static int mv_port_start(struct ata_port *ap)
                return -ENOMEM;
        ap->private_data = pp;
 
-       pp->crqb = dma_pool_alloc(hpriv->crqb_pool, GFP_KERNEL, &pp->crqb_dma);
+       pp->crqb = dma_pool_zalloc(hpriv->crqb_pool, GFP_KERNEL, &pp->crqb_dma);
        if (!pp->crqb)
                return -ENOMEM;
-       memset(pp->crqb, 0, MV_CRQB_Q_SZ);
 
-       pp->crpb = dma_pool_alloc(hpriv->crpb_pool, GFP_KERNEL, &pp->crpb_dma);
+       pp->crpb = dma_pool_zalloc(hpriv->crpb_pool, GFP_KERNEL, &pp->crpb_dma);
        if (!pp->crpb)
                goto out_port_free_dma_mem;
-       memset(pp->crpb, 0, MV_CRPB_Q_SZ);
 
        /* 6041/6081 Rev. "C0" (and newer) are okay with async notify */
        if (hpriv->hp_flags & MV_HP_ERRATA_60X1C0)
index c07e725ea93db3ffdffb933917b0be456911b724..10e1b9eee10eaf6ab4e5ac82b81d2ecfea83c10a 100644 (file)
@@ -119,4 +119,13 @@ config CFAG12864B_RATE
          If you compile this as a module, you can still override this
          value using the module parameters.
 
+config IMG_ASCII_LCD
+       tristate "Imagination Technologies ASCII LCD Display"
+       default y if MIPS_MALTA || MIPS_SEAD3
+       select SYSCON
+       help
+         Enable this to support the simple ASCII LCD displays found on
+         development boards such as the MIPS Boston, MIPS Malta & MIPS SEAD3
+         from Imagination Technologies.
+
 endif # AUXDISPLAY
index 8a8936a468b9f3acf446a5f250da8e73c629b6ab..3127175c89df53769ea7cdf50a1e2ed13f216fa2 100644 (file)
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_KS0108)           += ks0108.o
 obj-$(CONFIG_CFAG12864B)       += cfag12864b.o cfag12864bfb.o
+obj-$(CONFIG_IMG_ASCII_LCD)    += img-ascii-lcd.o
diff --git a/drivers/auxdisplay/img-ascii-lcd.c b/drivers/auxdisplay/img-ascii-lcd.c
new file mode 100644 (file)
index 0000000..bf43b5d
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.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 <generated/utsrelease.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+struct img_ascii_lcd_ctx;
+
+/**
+ * struct img_ascii_lcd_config - Configuration information about an LCD model
+ * @num_chars: the number of characters the LCD can display
+ * @external_regmap: true if registers are in a system controller, else false
+ * @update: function called to update the LCD
+ */
+struct img_ascii_lcd_config {
+       unsigned int num_chars;
+       bool external_regmap;
+       void (*update)(struct img_ascii_lcd_ctx *ctx);
+};
+
+/**
+ * struct img_ascii_lcd_ctx - Private data structure
+ * @pdev: the ASCII LCD platform device
+ * @base: the base address of the LCD registers
+ * @regmap: the regmap through which LCD registers are accessed
+ * @offset: the offset within regmap to the start of the LCD registers
+ * @cfg: pointer to the LCD model configuration
+ * @message: the full message to display or scroll on the LCD
+ * @message_len: the length of the @message string
+ * @scroll_pos: index of the first character of @message currently displayed
+ * @scroll_rate: scroll interval in jiffies
+ * @timer: timer used to implement scrolling
+ * @curr: the string currently displayed on the LCD
+ */
+struct img_ascii_lcd_ctx {
+       struct platform_device *pdev;
+       union {
+               void __iomem *base;
+               struct regmap *regmap;
+       };
+       u32 offset;
+       const struct img_ascii_lcd_config *cfg;
+       char *message;
+       unsigned int message_len;
+       unsigned int scroll_pos;
+       unsigned int scroll_rate;
+       struct timer_list timer;
+       char curr[] __aligned(8);
+};
+
+/*
+ * MIPS Boston development board
+ */
+
+static void boston_update(struct img_ascii_lcd_ctx *ctx)
+{
+       ulong val;
+
+#if BITS_PER_LONG == 64
+       val = *((u64 *)&ctx->curr[0]);
+       __raw_writeq(val, ctx->base);
+#elif BITS_PER_LONG == 32
+       val = *((u32 *)&ctx->curr[0]);
+       __raw_writel(val, ctx->base);
+       val = *((u32 *)&ctx->curr[4]);
+       __raw_writel(val, ctx->base + 4);
+#else
+# error Not 32 or 64 bit
+#endif
+}
+
+static struct img_ascii_lcd_config boston_config = {
+       .num_chars = 8,
+       .update = boston_update,
+};
+
+/*
+ * MIPS Malta development board
+ */
+
+static void malta_update(struct img_ascii_lcd_ctx *ctx)
+{
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < ctx->cfg->num_chars; i++) {
+               err = regmap_write(ctx->regmap,
+                                  ctx->offset + (i * 8), ctx->curr[i]);
+               if (err)
+                       break;
+       }
+
+       if (unlikely(err))
+               pr_err_ratelimited("Failed to update LCD display: %d\n", err);
+}
+
+static struct img_ascii_lcd_config malta_config = {
+       .num_chars = 8,
+       .external_regmap = true,
+       .update = malta_update,
+};
+
+/*
+ * MIPS SEAD3 development board
+ */
+
+enum {
+       SEAD3_REG_LCD_CTRL              = 0x00,
+#define SEAD3_REG_LCD_CTRL_SETDRAM     BIT(7)
+       SEAD3_REG_LCD_DATA              = 0x08,
+       SEAD3_REG_CPLD_STATUS           = 0x10,
+#define SEAD3_REG_CPLD_STATUS_BUSY     BIT(0)
+       SEAD3_REG_CPLD_DATA             = 0x18,
+#define SEAD3_REG_CPLD_DATA_BUSY       BIT(7)
+};
+
+static int sead3_wait_sm_idle(struct img_ascii_lcd_ctx *ctx)
+{
+       unsigned int status;
+       int err;
+
+       do {
+               err = regmap_read(ctx->regmap,
+                                 ctx->offset + SEAD3_REG_CPLD_STATUS,
+                                 &status);
+               if (err)
+                       return err;
+       } while (status & SEAD3_REG_CPLD_STATUS_BUSY);
+
+       return 0;
+
+}
+
+static int sead3_wait_lcd_idle(struct img_ascii_lcd_ctx *ctx)
+{
+       unsigned int cpld_data;
+       int err;
+
+       err = sead3_wait_sm_idle(ctx);
+       if (err)
+               return err;
+
+       do {
+               err = regmap_read(ctx->regmap,
+                                 ctx->offset + SEAD3_REG_LCD_CTRL,
+                                 &cpld_data);
+               if (err)
+                       return err;
+
+               err = sead3_wait_sm_idle(ctx);
+               if (err)
+                       return err;
+
+               err = regmap_read(ctx->regmap,
+                                 ctx->offset + SEAD3_REG_CPLD_DATA,
+                                 &cpld_data);
+               if (err)
+                       return err;
+       } while (cpld_data & SEAD3_REG_CPLD_DATA_BUSY);
+
+       return 0;
+}
+
+static void sead3_update(struct img_ascii_lcd_ctx *ctx)
+{
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < ctx->cfg->num_chars; i++) {
+               err = sead3_wait_lcd_idle(ctx);
+               if (err)
+                       break;
+
+               err = regmap_write(ctx->regmap,
+                                  ctx->offset + SEAD3_REG_LCD_CTRL,
+                                  SEAD3_REG_LCD_CTRL_SETDRAM | i);
+               if (err)
+                       break;
+
+               err = sead3_wait_lcd_idle(ctx);
+               if (err)
+                       break;
+
+               err = regmap_write(ctx->regmap,
+                                  ctx->offset + SEAD3_REG_LCD_DATA,
+                                  ctx->curr[i]);
+               if (err)
+                       break;
+       }
+
+       if (unlikely(err))
+               pr_err_ratelimited("Failed to update LCD display: %d\n", err);
+}
+
+static struct img_ascii_lcd_config sead3_config = {
+       .num_chars = 16,
+       .external_regmap = true,
+       .update = sead3_update,
+};
+
+static const struct of_device_id img_ascii_lcd_matches[] = {
+       { .compatible = "img,boston-lcd", .data = &boston_config },
+       { .compatible = "mti,malta-lcd", .data = &malta_config },
+       { .compatible = "mti,sead3-lcd", .data = &sead3_config },
+};
+
+/**
+ * img_ascii_lcd_scroll() - scroll the display by a character
+ * @arg: really a pointer to the private data structure
+ *
+ * Scroll the current message along the LCD by one character, rearming the
+ * timer if required.
+ */
+static void img_ascii_lcd_scroll(unsigned long arg)
+{
+       struct img_ascii_lcd_ctx *ctx = (struct img_ascii_lcd_ctx *)arg;
+       unsigned int i, ch = ctx->scroll_pos;
+       unsigned int num_chars = ctx->cfg->num_chars;
+
+       /* update the current message string */
+       for (i = 0; i < num_chars;) {
+               /* copy as many characters from the string as possible */
+               for (; i < num_chars && ch < ctx->message_len; i++, ch++)
+                       ctx->curr[i] = ctx->message[ch];
+
+               /* wrap around to the start of the string */
+               ch = 0;
+       }
+
+       /* update the LCD */
+       ctx->cfg->update(ctx);
+
+       /* move on to the next character */
+       ctx->scroll_pos++;
+       ctx->scroll_pos %= ctx->message_len;
+
+       /* rearm the timer */
+       if (ctx->message_len > ctx->cfg->num_chars)
+               mod_timer(&ctx->timer, jiffies + ctx->scroll_rate);
+}
+
+/**
+ * img_ascii_lcd_display() - set the message to be displayed
+ * @ctx: pointer to the private data structure
+ * @msg: the message to display
+ * @count: length of msg, or -1
+ *
+ * Display a new message @msg on the LCD. @msg can be longer than the number of
+ * characters the LCD can display, in which case it will begin scrolling across
+ * the LCD display.
+ *
+ * Return: 0 on success, -ENOMEM on memory allocation failure
+ */
+static int img_ascii_lcd_display(struct img_ascii_lcd_ctx *ctx,
+                            const char *msg, ssize_t count)
+{
+       char *new_msg;
+
+       /* stop the scroll timer */
+       del_timer_sync(&ctx->timer);
+
+       if (count == -1)
+               count = strlen(msg);
+
+       /* if the string ends with a newline, trim it */
+       if (msg[count - 1] == '\n')
+               count--;
+
+       new_msg = devm_kmalloc(&ctx->pdev->dev, count + 1, GFP_KERNEL);
+       if (!new_msg)
+               return -ENOMEM;
+
+       memcpy(new_msg, msg, count);
+       new_msg[count] = 0;
+
+       if (ctx->message)
+               devm_kfree(&ctx->pdev->dev, ctx->message);
+
+       ctx->message = new_msg;
+       ctx->message_len = count;
+       ctx->scroll_pos = 0;
+
+       /* update the LCD */
+       img_ascii_lcd_scroll((unsigned long)ctx);
+
+       return 0;
+}
+
+/**
+ * message_show() - read message via sysfs
+ * @dev: the LCD device
+ * @attr: the LCD message attribute
+ * @buf: the buffer to read the message into
+ *
+ * Read the current message being displayed or scrolled across the LCD display
+ * into @buf, for reads from sysfs.
+ *
+ * Return: the number of characters written to @buf
+ */
+static ssize_t message_show(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       struct img_ascii_lcd_ctx *ctx = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%s\n", ctx->message);
+}
+
+/**
+ * message_store() - write a new message via sysfs
+ * @dev: the LCD device
+ * @attr: the LCD message attribute
+ * @buf: the buffer containing the new message
+ * @count: the size of the message in @buf
+ *
+ * Write a new message to display or scroll across the LCD display from sysfs.
+ *
+ * Return: the size of the message on success, else -ERRNO
+ */
+static ssize_t message_store(struct device *dev, struct device_attribute *attr,
+                            const char *buf, size_t count)
+{
+       struct img_ascii_lcd_ctx *ctx = dev_get_drvdata(dev);
+       int err;
+
+       err = img_ascii_lcd_display(ctx, buf, count);
+       return err ?: count;
+}
+
+static DEVICE_ATTR_RW(message);
+
+/**
+ * img_ascii_lcd_probe() - probe an LCD display device
+ * @pdev: the LCD platform device
+ *
+ * Probe an LCD display device, ensuring that we have the required resources in
+ * order to access the LCD & setting up private data as well as sysfs files.
+ *
+ * Return: 0 on success, else -ERRNO
+ */
+static int img_ascii_lcd_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *match;
+       const struct img_ascii_lcd_config *cfg;
+       struct img_ascii_lcd_ctx *ctx;
+       struct resource *res;
+       int err;
+
+       match = of_match_device(img_ascii_lcd_matches, &pdev->dev);
+       if (!match)
+               return -ENODEV;
+
+       cfg = match->data;
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx) + cfg->num_chars,
+                          GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       if (cfg->external_regmap) {
+               ctx->regmap = syscon_node_to_regmap(pdev->dev.parent->of_node);
+               if (IS_ERR(ctx->regmap))
+                       return PTR_ERR(ctx->regmap);
+
+               if (of_property_read_u32(pdev->dev.of_node, "offset",
+                                        &ctx->offset))
+                       return -EINVAL;
+       } else {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               ctx->base = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(ctx->base))
+                       return PTR_ERR(ctx->base);
+       }
+
+       ctx->pdev = pdev;
+       ctx->cfg = cfg;
+       ctx->message = NULL;
+       ctx->scroll_pos = 0;
+       ctx->scroll_rate = HZ / 2;
+
+       /* initialise a timer for scrolling the message */
+       init_timer(&ctx->timer);
+       ctx->timer.function = img_ascii_lcd_scroll;
+       ctx->timer.data = (unsigned long)ctx;
+
+       platform_set_drvdata(pdev, ctx);
+
+       /* display a default message */
+       err = img_ascii_lcd_display(ctx, "Linux " UTS_RELEASE "       ", -1);
+       if (err)
+               goto out_del_timer;
+
+       err = device_create_file(&pdev->dev, &dev_attr_message);
+       if (err)
+               goto out_del_timer;
+
+       return 0;
+out_del_timer:
+       del_timer_sync(&ctx->timer);
+       return err;
+}
+
+/**
+ * img_ascii_lcd_remove() - remove an LCD display device
+ * @pdev: the LCD platform device
+ *
+ * Remove an LCD display device, freeing private resources & ensuring that the
+ * driver stops using the LCD display registers.
+ *
+ * Return: 0
+ */
+static int img_ascii_lcd_remove(struct platform_device *pdev)
+{
+       struct img_ascii_lcd_ctx *ctx = platform_get_drvdata(pdev);
+
+       device_remove_file(&pdev->dev, &dev_attr_message);
+       del_timer_sync(&ctx->timer);
+       return 0;
+}
+
+static struct platform_driver img_ascii_lcd_driver = {
+       .driver = {
+               .name           = "img-ascii-lcd",
+               .of_match_table = img_ascii_lcd_matches,
+       },
+       .probe  = img_ascii_lcd_probe,
+       .remove = img_ascii_lcd_remove,
+};
+module_platform_driver(img_ascii_lcd_driver);
index d131e152c8ce6a3e70a7e0fe287275269f0c40c6..d6876d50622075f1b490bf7a19831b4e8784adb6 100644 (file)
@@ -479,8 +479,8 @@ static ssize_t _extract_entropy(struct entropy_store *r, void *buf,
 
 static void crng_reseed(struct crng_state *crng, struct entropy_store *r);
 static void push_to_pool(struct work_struct *work);
-static __u32 input_pool_data[INPUT_POOL_WORDS];
-static __u32 blocking_pool_data[OUTPUT_POOL_WORDS];
+static __u32 input_pool_data[INPUT_POOL_WORDS] __latent_entropy;
+static __u32 blocking_pool_data[OUTPUT_POOL_WORDS] __latent_entropy;
 
 static struct entropy_store input_pool = {
        .poolinfo = &poolinfo_table[0],
index 1b2f28f69a8142fce5edcd02c2f85f2e4f0c4a72..4852d9efe74e7c470169b25babbb3355e8a5a0b6 100644 (file)
@@ -80,11 +80,17 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
 {
        struct cppc_cpudata *cpu;
        struct cpufreq_freqs freqs;
+       u32 desired_perf;
        int ret = 0;
 
        cpu = all_cpu_data[policy->cpu];
 
-       cpu->perf_ctrls.desired_perf = (u64)target_freq * policy->max / cppc_dmi_max_khz;
+       desired_perf = (u64)target_freq * cpu->perf_caps.highest_perf / cppc_dmi_max_khz;
+       /* Return if it is exactly the same perf */
+       if (desired_perf == cpu->perf_ctrls.desired_perf)
+               return ret;
+
+       cpu->perf_ctrls.desired_perf = desired_perf;
        freqs.old = policy->cur;
        freqs.new = target_freq;
 
index 18da4f8051d372d06eb74e68e591f57e40569c82..13475890d792d7bb24209a8fa8617f011883e660 100644 (file)
@@ -17,6 +17,7 @@
 struct cs_policy_dbs_info {
        struct policy_dbs_info policy_dbs;
        unsigned int down_skip;
+       unsigned int requested_freq;
 };
 
 static inline struct cs_policy_dbs_info *to_dbs_info(struct policy_dbs_info *policy_dbs)
@@ -61,6 +62,7 @@ static unsigned int cs_dbs_timer(struct cpufreq_policy *policy)
 {
        struct policy_dbs_info *policy_dbs = policy->governor_data;
        struct cs_policy_dbs_info *dbs_info = to_dbs_info(policy_dbs);
+       unsigned int requested_freq = dbs_info->requested_freq;
        struct dbs_data *dbs_data = policy_dbs->dbs_data;
        struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
        unsigned int load = dbs_update(policy);
@@ -72,10 +74,16 @@ static unsigned int cs_dbs_timer(struct cpufreq_policy *policy)
        if (cs_tuners->freq_step == 0)
                goto out;
 
+       /*
+        * If requested_freq is out of range, it is likely that the limits
+        * changed in the meantime, so fall back to current frequency in that
+        * case.
+        */
+       if (requested_freq > policy->max || requested_freq < policy->min)
+               requested_freq = policy->cur;
+
        /* Check for frequency increase */
        if (load > dbs_data->up_threshold) {
-               unsigned int requested_freq = policy->cur;
-
                dbs_info->down_skip = 0;
 
                /* if we are already at full speed then break out early */
@@ -83,8 +91,11 @@ static unsigned int cs_dbs_timer(struct cpufreq_policy *policy)
                        goto out;
 
                requested_freq += get_freq_target(cs_tuners, policy);
+               if (requested_freq > policy->max)
+                       requested_freq = policy->max;
 
                __cpufreq_driver_target(policy, requested_freq, CPUFREQ_RELATION_H);
+               dbs_info->requested_freq = requested_freq;
                goto out;
        }
 
@@ -95,7 +106,7 @@ static unsigned int cs_dbs_timer(struct cpufreq_policy *policy)
 
        /* Check for frequency decrease */
        if (load < cs_tuners->down_threshold) {
-               unsigned int freq_target, requested_freq = policy->cur;
+               unsigned int freq_target;
                /*
                 * if we cannot reduce the frequency anymore, break out early
                 */
@@ -109,6 +120,7 @@ static unsigned int cs_dbs_timer(struct cpufreq_policy *policy)
                        requested_freq = policy->min;
 
                __cpufreq_driver_target(policy, requested_freq, CPUFREQ_RELATION_L);
+               dbs_info->requested_freq = requested_freq;
        }
 
  out:
@@ -287,6 +299,7 @@ static void cs_start(struct cpufreq_policy *policy)
        struct cs_policy_dbs_info *dbs_info = to_dbs_info(policy->governor_data);
 
        dbs_info->down_skip = 0;
+       dbs_info->requested_freq = policy->cur;
 }
 
 static struct dbs_governor cs_governor = {
index 806f2039571e56ec69bd2cad697f788821826c1a..f535f812325841a683d5691fc61bd89860681229 100644 (file)
@@ -225,7 +225,7 @@ struct cpudata {
 static struct cpudata **all_cpu_data;
 
 /**
- * struct pid_adjust_policy - Stores static PID configuration data
+ * struct pstate_adjust_policy - Stores static PID configuration data
  * @sample_rate_ms:    PID calculation sample rate in ms
  * @sample_rate_ns:    Sample rate calculation in ns
  * @deadband:          PID deadband
@@ -562,12 +562,12 @@ static void intel_pstate_hwp_set(const struct cpumask *cpumask)
        int min, hw_min, max, hw_max, cpu, range, adj_range;
        u64 value, cap;
 
-       rdmsrl(MSR_HWP_CAPABILITIES, cap);
-       hw_min = HWP_LOWEST_PERF(cap);
-       hw_max = HWP_HIGHEST_PERF(cap);
-       range = hw_max - hw_min;
-
        for_each_cpu(cpu, cpumask) {
+               rdmsrl_on_cpu(cpu, MSR_HWP_CAPABILITIES, &cap);
+               hw_min = HWP_LOWEST_PERF(cap);
+               hw_max = HWP_HIGHEST_PERF(cap);
+               range = hw_max - hw_min;
+
                rdmsrl_on_cpu(cpu, MSR_HWP_REQUEST, &value);
                adj_range = limits->min_perf_pct * range / 100;
                min = hw_min + adj_range;
@@ -1232,6 +1232,7 @@ static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu)
 {
        struct sample *sample = &cpu->sample;
        int32_t busy_frac, boost;
+       int target, avg_pstate;
 
        busy_frac = div_fp(sample->mperf, sample->tsc);
 
@@ -1242,7 +1243,26 @@ static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu)
                busy_frac = boost;
 
        sample->busy_scaled = busy_frac * 100;
-       return get_avg_pstate(cpu) - pid_calc(&cpu->pid, sample->busy_scaled);
+
+       target = limits->no_turbo || limits->turbo_disabled ?
+                       cpu->pstate.max_pstate : cpu->pstate.turbo_pstate;
+       target += target >> 2;
+       target = mul_fp(target, busy_frac);
+       if (target < cpu->pstate.min_pstate)
+               target = cpu->pstate.min_pstate;
+
+       /*
+        * If the average P-state during the previous cycle was higher than the
+        * current target, add 50% of the difference to the target to reduce
+        * possible performance oscillations and offset possible performance
+        * loss related to moving the workload from one CPU to another within
+        * a package/module.
+        */
+       avg_pstate = get_avg_pstate(cpu);
+       if (avg_pstate > target)
+               target += (avg_pstate - target) >> 1;
+
+       return target;
 }
 
 static inline int32_t get_target_pstate_use_performance(struct cpudata *cpu)
@@ -1251,10 +1271,11 @@ static inline int32_t get_target_pstate_use_performance(struct cpudata *cpu)
        u64 duration_ns;
 
        /*
-        * perf_scaled is the average performance during the last sampling
-        * period scaled by the ratio of the maximum P-state to the P-state
-        * requested last time (in percent).  That measures the system's
-        * response to the previous P-state selection.
+        * perf_scaled is the ratio of the average P-state during the last
+        * sampling period to the P-state requested last time (in percent).
+        *
+        * That measures the system's response to the previous P-state
+        * selection.
         */
        max_pstate = cpu->pstate.max_pstate_physical;
        current_pstate = cpu->pstate.current_pstate;
index 4102be01d06a03db5d98f91473b0083db81158d7..512ee37b374b1cf82b4762ccde10d233d35b98a0 100644 (file)
@@ -5,7 +5,7 @@ config MIPS_CPS_CPUIDLE
        bool "CPU Idle driver for MIPS CPS platforms"
        depends on CPU_IDLE && MIPS_CPS
        depends on SYS_SUPPORTS_MIPS_CPS
-       select ARCH_NEEDS_CPU_IDLE_COUPLED if MIPS_MT
+       select ARCH_NEEDS_CPU_IDLE_COUPLED if MIPS_MT || CPU_MIPSR6
        select GENERIC_CLOCKEVENTS_BROADCAST if SMP
        select MIPS_CPS_PM
        default y
index 1adb6980b707ced6b9737e1d1df79ce10af41def..926ba9871c628ac4e1e352363f578cfc22835788 100644 (file)
@@ -163,7 +163,7 @@ static int __init cps_cpuidle_init(void)
                core = cpu_data[cpu].core;
                device = &per_cpu(cpuidle_dev, cpu);
                device->cpu = cpu;
-#ifdef CONFIG_MIPS_MT
+#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
                cpumask_copy(&device->coupled_cpus, &cpu_sibling_map[cpu]);
 #endif
 
index 478006b7764a5919259e8b582dedfd3a1f5358f5..bf3ea7603a58a9705d51feb271a61507f5c779e9 100644 (file)
@@ -137,6 +137,10 @@ static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 
        cur_time = jiffies;
 
+       /* Immediately exit if previous_freq is not initialized yet. */
+       if (!devfreq->previous_freq)
+               goto out;
+
        prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq);
        if (prev_lev < 0) {
                ret = prev_lev;
@@ -594,17 +598,19 @@ struct devfreq *devfreq_add_device(struct device *dev,
        if (devfreq->governor)
                err = devfreq->governor->event_handler(devfreq,
                                        DEVFREQ_GOV_START, NULL);
-       mutex_unlock(&devfreq_list_lock);
        if (err) {
                dev_err(dev, "%s: Unable to start governor for the device\n",
                        __func__);
                goto err_init;
        }
+       mutex_unlock(&devfreq_list_lock);
 
        return devfreq;
 
 err_init:
        list_del(&devfreq->node);
+       mutex_unlock(&devfreq_list_lock);
+
        device_unregister(&devfreq->dev);
 err_out:
        return ERR_PTR(err);
index 0fdae86089613fe130d35246318fe75d41a755c5..cd949800eed962cffa34b599d3ce66f386763d11 100644 (file)
@@ -17,6 +17,7 @@ config DEVFREQ_EVENT_EXYNOS_NOCP
        tristate "EXYNOS NoC (Network On Chip) Probe DEVFREQ event Driver"
        depends on ARCH_EXYNOS || COMPILE_TEST
        select PM_OPP
+       select REGMAP_MMIO
        help
          This add the devfreq-event driver for Exynos SoC. It provides NoC
          (Network on Chip) Probe counters to measure the bandwidth of AXI bus.
index a5841403bde8b64c314c921148b6189d7dcef82f..49e712aca0c15367e2168d91a3542cdb6ff2ff5b 100644 (file)
@@ -176,9 +176,6 @@ static int exynos_nocp_get_event(struct devfreq_event_dev *edev,
        return 0;
 
 out:
-       edata->load_count = 0;
-       edata->total_count = 0;
-
        dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n");
 
        return ret;
index 631c977b0da5f817b7ecf950d94f90198a53abbe..180f0a96528cee063d3da64fa620d6de0eebdfcf 100644 (file)
@@ -566,6 +566,11 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused)
 
        lynx->registers = ioremap_nocache(pci_resource_start(dev, 0),
                                          PCILYNX_MAX_REGISTER);
+       if (lynx->registers == NULL) {
+               dev_err(&dev->dev, "Failed to map registers\n");
+               ret = -ENOMEM;
+               goto fail_deallocate_lynx;
+       }
 
        lynx->rcv_start_pcl = pci_alloc_consistent(lynx->pci_device,
                                sizeof(struct pcl), &lynx->rcv_start_pcl_bus);
@@ -578,7 +583,7 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused)
            lynx->rcv_buffer == NULL) {
                dev_err(&dev->dev, "Failed to allocate receive buffer\n");
                ret = -ENOMEM;
-               goto fail_deallocate;
+               goto fail_deallocate_buffers;
        }
        lynx->rcv_start_pcl->next       = cpu_to_le32(lynx->rcv_pcl_bus);
        lynx->rcv_pcl->next             = cpu_to_le32(PCL_NEXT_INVALID);
@@ -641,7 +646,7 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused)
                dev_err(&dev->dev,
                        "Failed to allocate shared interrupt %d\n", dev->irq);
                ret = -EIO;
-               goto fail_deallocate;
+               goto fail_deallocate_buffers;
        }
 
        lynx->misc.parent = &dev->dev;
@@ -668,7 +673,7 @@ fail_free_irq:
        reg_write(lynx, PCI_INT_ENABLE, 0);
        free_irq(lynx->pci_device->irq, lynx);
 
-fail_deallocate:
+fail_deallocate_buffers:
        if (lynx->rcv_start_pcl)
                pci_free_consistent(lynx->pci_device, sizeof(struct pcl),
                                lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus);
@@ -679,6 +684,8 @@ fail_deallocate:
                pci_free_consistent(lynx->pci_device, PAGE_SIZE,
                                lynx->rcv_buffer, lynx->rcv_buffer_bus);
        iounmap(lynx->registers);
+
+fail_deallocate_lynx:
        kfree(lynx);
 
 fail_disable:
index 45c8817d068c60649ee9e5648dbf9451f3bf5667..e422568e14ad19273c277fc1565d068ab2819c53 100644 (file)
@@ -794,6 +794,22 @@ static int pca953x_probe(struct i2c_client *client,
        }
 
        mutex_init(&chip->i2c_lock);
+       /*
+        * In case we have an i2c-mux controlled by a GPIO provided by an
+        * expander using the same driver higher on the device tree, read the
+        * i2c adapter nesting depth and use the retrieved value as lockdep
+        * subclass for chip->i2c_lock.
+        *
+        * REVISIT: This solution is not complete. It protects us from lockdep
+        * false positives when the expander controlling the i2c-mux is on
+        * a different level on the device tree, but not when it's on the same
+        * level on a different branch (in which case the subclass number
+        * would be the same).
+        *
+        * TODO: Once a correct solution is developed, a similar fix should be
+        * applied to all other i2c-controlled GPIO expanders (and potentially
+        * regmap-i2c).
+        */
        lockdep_set_subclass(&chip->i2c_lock,
                             i2c_adapter_depth(client->adapter));
 
index 2e3a0543760d0967164b2a5833cf9c39a63bf90b..e3281d4e3e414cc9d693932911215c6fc7216d6f 100644 (file)
@@ -765,7 +765,7 @@ amdgpu_connector_lvds_detect(struct drm_connector *connector, bool force)
        return ret;
 }
 
-static void amdgpu_connector_destroy(struct drm_connector *connector)
+static void amdgpu_connector_unregister(struct drm_connector *connector)
 {
        struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
@@ -773,6 +773,12 @@ static void amdgpu_connector_destroy(struct drm_connector *connector)
                drm_dp_aux_unregister(&amdgpu_connector->ddc_bus->aux);
                amdgpu_connector->ddc_bus->has_aux = false;
        }
+}
+
+static void amdgpu_connector_destroy(struct drm_connector *connector)
+{
+       struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
+
        amdgpu_connector_free_edid(connector);
        kfree(amdgpu_connector->con_priv);
        drm_connector_unregister(connector);
@@ -826,6 +832,7 @@ static const struct drm_connector_funcs amdgpu_connector_lvds_funcs = {
        .dpms = drm_helper_connector_dpms,
        .detect = amdgpu_connector_lvds_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
+       .early_unregister = amdgpu_connector_unregister,
        .destroy = amdgpu_connector_destroy,
        .set_property = amdgpu_connector_set_lcd_property,
 };
@@ -936,6 +943,7 @@ static const struct drm_connector_funcs amdgpu_connector_vga_funcs = {
        .dpms = drm_helper_connector_dpms,
        .detect = amdgpu_connector_vga_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
+       .early_unregister = amdgpu_connector_unregister,
        .destroy = amdgpu_connector_destroy,
        .set_property = amdgpu_connector_set_property,
 };
@@ -1203,6 +1211,7 @@ static const struct drm_connector_funcs amdgpu_connector_dvi_funcs = {
        .detect = amdgpu_connector_dvi_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = amdgpu_connector_set_property,
+       .early_unregister = amdgpu_connector_unregister,
        .destroy = amdgpu_connector_destroy,
        .force = amdgpu_connector_dvi_force,
 };
@@ -1493,6 +1502,7 @@ static const struct drm_connector_funcs amdgpu_connector_dp_funcs = {
        .detect = amdgpu_connector_dp_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = amdgpu_connector_set_property,
+       .early_unregister = amdgpu_connector_unregister,
        .destroy = amdgpu_connector_destroy,
        .force = amdgpu_connector_dvi_force,
 };
@@ -1502,6 +1512,7 @@ static const struct drm_connector_funcs amdgpu_connector_edp_funcs = {
        .detect = amdgpu_connector_dp_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = amdgpu_connector_set_lcd_property,
+       .early_unregister = amdgpu_connector_unregister,
        .destroy = amdgpu_connector_destroy,
        .force = amdgpu_connector_dvi_force,
 };
index e203e5561107146badbb45a2260763c87e06f8e0..a5e2fcbef0f0f24f54bcf54164eb610c463edd49 100644 (file)
@@ -43,6 +43,9 @@ static int amdgpu_ctx_init(struct amdgpu_device *adev, struct amdgpu_ctx *ctx)
                ctx->rings[i].sequence = 1;
                ctx->rings[i].fences = &ctx->fences[amdgpu_sched_jobs * i];
        }
+
+       ctx->reset_counter = atomic_read(&adev->gpu_reset_counter);
+
        /* create context entity for each ring */
        for (i = 0; i < adev->num_rings; i++) {
                struct amdgpu_ring *ring = adev->rings[i];
index 7dbe85d67d2682854e8158e35f9ee8f8e0e65d94..b4f4a9239069d1a3f073cd89f1355b2e0dd7e985 100644 (file)
@@ -1408,16 +1408,6 @@ static int amdgpu_late_init(struct amdgpu_device *adev)
        for (i = 0; i < adev->num_ip_blocks; i++) {
                if (!adev->ip_block_status[i].valid)
                        continue;
-               if (adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_UVD ||
-                       adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_VCE)
-                       continue;
-               /* enable clockgating to save power */
-               r = adev->ip_blocks[i].funcs->set_clockgating_state((void *)adev,
-                                                                   AMD_CG_STATE_GATE);
-               if (r) {
-                       DRM_ERROR("set_clockgating_state(gate) of IP block <%s> failed %d\n", adev->ip_blocks[i].funcs->name, r);
-                       return r;
-               }
                if (adev->ip_blocks[i].funcs->late_init) {
                        r = adev->ip_blocks[i].funcs->late_init((void *)adev);
                        if (r) {
@@ -1426,6 +1416,18 @@ static int amdgpu_late_init(struct amdgpu_device *adev)
                        }
                        adev->ip_block_status[i].late_initialized = true;
                }
+               /* skip CG for VCE/UVD, it's handled specially */
+               if (adev->ip_blocks[i].type != AMD_IP_BLOCK_TYPE_UVD &&
+                   adev->ip_blocks[i].type != AMD_IP_BLOCK_TYPE_VCE) {
+                       /* enable clockgating to save power */
+                       r = adev->ip_blocks[i].funcs->set_clockgating_state((void *)adev,
+                                                                           AMD_CG_STATE_GATE);
+                       if (r) {
+                               DRM_ERROR("set_clockgating_state(gate) of IP block <%s> failed %d\n",
+                                         adev->ip_blocks[i].funcs->name, r);
+                               return r;
+                       }
+               }
        }
 
        return 0;
@@ -1435,6 +1437,30 @@ static int amdgpu_fini(struct amdgpu_device *adev)
 {
        int i, r;
 
+       /* need to disable SMC first */
+       for (i = 0; i < adev->num_ip_blocks; i++) {
+               if (!adev->ip_block_status[i].hw)
+                       continue;
+               if (adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_SMC) {
+                       /* ungate blocks before hw fini so that we can shutdown the blocks safely */
+                       r = adev->ip_blocks[i].funcs->set_clockgating_state((void *)adev,
+                                                                           AMD_CG_STATE_UNGATE);
+                       if (r) {
+                               DRM_ERROR("set_clockgating_state(ungate) of IP block <%s> failed %d\n",
+                                         adev->ip_blocks[i].funcs->name, r);
+                               return r;
+                       }
+                       r = adev->ip_blocks[i].funcs->hw_fini((void *)adev);
+                       /* XXX handle errors */
+                       if (r) {
+                               DRM_DEBUG("hw_fini of IP block <%s> failed %d\n",
+                                         adev->ip_blocks[i].funcs->name, r);
+                       }
+                       adev->ip_block_status[i].hw = false;
+                       break;
+               }
+       }
+
        for (i = adev->num_ip_blocks - 1; i >= 0; i--) {
                if (!adev->ip_block_status[i].hw)
                        continue;
@@ -2073,7 +2099,8 @@ static bool amdgpu_check_soft_reset(struct amdgpu_device *adev)
                if (!adev->ip_block_status[i].valid)
                        continue;
                if (adev->ip_blocks[i].funcs->check_soft_reset)
-                       adev->ip_blocks[i].funcs->check_soft_reset(adev);
+                       adev->ip_block_status[i].hang =
+                               adev->ip_blocks[i].funcs->check_soft_reset(adev);
                if (adev->ip_block_status[i].hang) {
                        DRM_INFO("IP block:%d is hang!\n", i);
                        asic_hang = true;
@@ -2102,12 +2129,20 @@ static int amdgpu_pre_soft_reset(struct amdgpu_device *adev)
 
 static bool amdgpu_need_full_reset(struct amdgpu_device *adev)
 {
-       if (adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang ||
-           adev->ip_block_status[AMD_IP_BLOCK_TYPE_SMC].hang ||
-           adev->ip_block_status[AMD_IP_BLOCK_TYPE_ACP].hang ||
-           adev->ip_block_status[AMD_IP_BLOCK_TYPE_DCE].hang) {
-               DRM_INFO("Some block need full reset!\n");
-               return true;
+       int i;
+
+       for (i = 0; i < adev->num_ip_blocks; i++) {
+               if (!adev->ip_block_status[i].valid)
+                       continue;
+               if ((adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_GMC) ||
+                   (adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_SMC) ||
+                   (adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_ACP) ||
+                   (adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_DCE)) {
+                       if (adev->ip_block_status[i].hang) {
+                               DRM_INFO("Some block need full reset!\n");
+                               return true;
+                       }
+               }
        }
        return false;
 }
index fe36caf1b7d7b084762d19fb7fa6516a42dd3fa3..14f57d9915e3fc0aa8d5a8e77b734073f05ecb9b 100644 (file)
@@ -113,24 +113,26 @@ void amdgpu_dpm_print_ps_status(struct amdgpu_device *adev,
        printk("\n");
 }
 
+
 u32 amdgpu_dpm_get_vblank_time(struct amdgpu_device *adev)
 {
        struct drm_device *dev = adev->ddev;
        struct drm_crtc *crtc;
        struct amdgpu_crtc *amdgpu_crtc;
-       u32 line_time_us, vblank_lines;
+       u32 vblank_in_pixels;
        u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */
 
        if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) {
                list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                        amdgpu_crtc = to_amdgpu_crtc(crtc);
                        if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) {
-                               line_time_us = (amdgpu_crtc->hw_mode.crtc_htotal * 1000) /
-                                       amdgpu_crtc->hw_mode.clock;
-                               vblank_lines = amdgpu_crtc->hw_mode.crtc_vblank_end -
+                               vblank_in_pixels =
+                                       amdgpu_crtc->hw_mode.crtc_htotal *
+                                       (amdgpu_crtc->hw_mode.crtc_vblank_end -
                                        amdgpu_crtc->hw_mode.crtc_vdisplay +
-                                       (amdgpu_crtc->v_border * 2);
-                               vblank_time_us = vblank_lines * line_time_us;
+                                       (amdgpu_crtc->v_border * 2));
+
+                               vblank_time_us = vblank_in_pixels * 1000 / amdgpu_crtc->hw_mode.clock;
                                break;
                        }
                }
index e1fa8731d1e2db7b16c09edf44d195e8d457cf07..3cb5e903cd62896529d2ea18d5ea7f49c2feae73 100644 (file)
@@ -345,8 +345,8 @@ static int amdgpu_debugfs_ring_init(struct amdgpu_device *adev,
        ent = debugfs_create_file(name,
                                  S_IFREG | S_IRUGO, root,
                                  ring, &amdgpu_debugfs_ring_fops);
-       if (IS_ERR(ent))
-               return PTR_ERR(ent);
+       if (!ent)
+               return -ENOMEM;
 
        i_size_write(ent->d_inode, ring->ring_size + 12);
        ring->ent = ent;
index 887483b8b818383c5139ec8c2059e3fd19f16014..dcaf691f56b5577352d2238bdfd27e2f1aca2386 100644 (file)
@@ -555,10 +555,13 @@ struct amdgpu_ttm_tt {
 int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages)
 {
        struct amdgpu_ttm_tt *gtt = (void *)ttm;
-       int write = !(gtt->userflags & AMDGPU_GEM_USERPTR_READONLY);
+       unsigned int flags = 0;
        unsigned pinned = 0;
        int r;
 
+       if (!(gtt->userflags & AMDGPU_GEM_USERPTR_READONLY))
+               flags |= FOLL_WRITE;
+
        if (gtt->userflags & AMDGPU_GEM_USERPTR_ANONONLY) {
                /* check that we only use anonymous memory
                   to prevent problems with writeback */
@@ -581,7 +584,7 @@ int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages)
                list_add(&guptask.list, &gtt->guptasks);
                spin_unlock(&gtt->guptasklock);
 
-               r = get_user_pages(userptr, num_pages, write, 0, p, NULL);
+               r = get_user_pages(userptr, num_pages, flags, p, NULL);
 
                spin_lock(&gtt->guptasklock);
                list_del(&guptask.list);
index f80a0834e889e8ff07b77846f28fcb619632ca14..3c082e1437303d3247da620fcd08ba8a031e62e0 100644 (file)
@@ -1514,14 +1514,16 @@ static int cz_dpm_set_powergating_state(void *handle,
        return 0;
 }
 
-/* borrowed from KV, need future unify */
 static int cz_dpm_get_temperature(struct amdgpu_device *adev)
 {
        int actual_temp = 0;
-       uint32_t temp = RREG32_SMC(0xC0300E0C);
+       uint32_t val = RREG32_SMC(ixTHM_TCON_CUR_TMP);
+       uint32_t temp = REG_GET_FIELD(val, THM_TCON_CUR_TMP, CUR_TEMP);
 
-       if (temp)
+       if (REG_GET_FIELD(val, THM_TCON_CUR_TMP, CUR_TEMP_RANGE_SEL))
                actual_temp = 1000 * ((temp / 8) - 49);
+       else
+               actual_temp = 1000 * (temp / 8);
 
        return actual_temp;
 }
index 613ebb7ed50f5e33699fb254bd417b238f6c2783..4108c686aa7c20619bcbe33d430dc8cbbc6fcbe7 100644 (file)
@@ -3188,16 +3188,11 @@ static int dce_v10_0_wait_for_idle(void *handle)
        return 0;
 }
 
-static int dce_v10_0_check_soft_reset(void *handle)
+static bool dce_v10_0_check_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       if (dce_v10_0_is_display_hung(adev))
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_DCE].hang = true;
-       else
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_DCE].hang = false;
-
-       return 0;
+       return dce_v10_0_is_display_hung(adev);
 }
 
 static int dce_v10_0_soft_reset(void *handle)
@@ -3205,9 +3200,6 @@ static int dce_v10_0_soft_reset(void *handle)
        u32 srbm_soft_reset = 0, tmp;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_DCE].hang)
-               return 0;
-
        if (dce_v10_0_is_display_hung(adev))
                srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_DC_MASK;
 
index 6c6ff57b1c95f2824537933c8c8ecd6525bda3ab..ee6a48a092143ae6952bbcce9af1f04dc5712614 100644 (file)
@@ -4087,14 +4087,21 @@ static int gfx_v8_0_rlc_load_microcode(struct amdgpu_device *adev)
 static int gfx_v8_0_rlc_resume(struct amdgpu_device *adev)
 {
        int r;
+       u32 tmp;
 
        gfx_v8_0_rlc_stop(adev);
 
        /* disable CG */
-       WREG32(mmRLC_CGCG_CGLS_CTRL, 0);
+       tmp = RREG32(mmRLC_CGCG_CGLS_CTRL);
+       tmp &= ~(RLC_CGCG_CGLS_CTRL__CGCG_EN_MASK |
+                RLC_CGCG_CGLS_CTRL__CGLS_EN_MASK);
+       WREG32(mmRLC_CGCG_CGLS_CTRL, tmp);
        if (adev->asic_type == CHIP_POLARIS11 ||
-           adev->asic_type == CHIP_POLARIS10)
-               WREG32(mmRLC_CGCG_CGLS_CTRL_3D, 0);
+           adev->asic_type == CHIP_POLARIS10) {
+               tmp = RREG32(mmRLC_CGCG_CGLS_CTRL_3D);
+               tmp &= ~0x3;
+               WREG32(mmRLC_CGCG_CGLS_CTRL_3D, tmp);
+       }
 
        /* disable PG */
        WREG32(mmRLC_PG_CNTL, 0);
@@ -5137,7 +5144,7 @@ static int gfx_v8_0_wait_for_idle(void *handle)
        return -ETIMEDOUT;
 }
 
-static int gfx_v8_0_check_soft_reset(void *handle)
+static bool gfx_v8_0_check_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
@@ -5189,16 +5196,14 @@ static int gfx_v8_0_check_soft_reset(void *handle)
                                                SRBM_SOFT_RESET, SOFT_RESET_SEM, 1);
 
        if (grbm_soft_reset || srbm_soft_reset) {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_GFX].hang = true;
                adev->gfx.grbm_soft_reset = grbm_soft_reset;
                adev->gfx.srbm_soft_reset = srbm_soft_reset;
+               return true;
        } else {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_GFX].hang = false;
                adev->gfx.grbm_soft_reset = 0;
                adev->gfx.srbm_soft_reset = 0;
+               return false;
        }
-
-       return 0;
 }
 
 static void gfx_v8_0_inactive_hqd(struct amdgpu_device *adev,
@@ -5226,7 +5231,8 @@ static int gfx_v8_0_pre_soft_reset(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GFX].hang)
+       if ((!adev->gfx.grbm_soft_reset) &&
+           (!adev->gfx.srbm_soft_reset))
                return 0;
 
        grbm_soft_reset = adev->gfx.grbm_soft_reset;
@@ -5264,7 +5270,8 @@ static int gfx_v8_0_soft_reset(void *handle)
        u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
        u32 tmp;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GFX].hang)
+       if ((!adev->gfx.grbm_soft_reset) &&
+           (!adev->gfx.srbm_soft_reset))
                return 0;
 
        grbm_soft_reset = adev->gfx.grbm_soft_reset;
@@ -5334,7 +5341,8 @@ static int gfx_v8_0_post_soft_reset(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GFX].hang)
+       if ((!adev->gfx.grbm_soft_reset) &&
+           (!adev->gfx.srbm_soft_reset))
                return 0;
 
        grbm_soft_reset = adev->gfx.grbm_soft_reset;
index 1b319f5bc6962d5d6250db12fcb18302db789ade..c22ef140a54215e5253b7c9605a358a4785f8c05 100644 (file)
@@ -1099,7 +1099,7 @@ static int gmc_v8_0_wait_for_idle(void *handle)
 
 }
 
-static int gmc_v8_0_check_soft_reset(void *handle)
+static bool gmc_v8_0_check_soft_reset(void *handle)
 {
        u32 srbm_soft_reset = 0;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
@@ -1116,20 +1116,19 @@ static int gmc_v8_0_check_soft_reset(void *handle)
                                                        SRBM_SOFT_RESET, SOFT_RESET_MC, 1);
        }
        if (srbm_soft_reset) {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang = true;
                adev->mc.srbm_soft_reset = srbm_soft_reset;
+               return true;
        } else {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang = false;
                adev->mc.srbm_soft_reset = 0;
+               return false;
        }
-       return 0;
 }
 
 static int gmc_v8_0_pre_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang)
+       if (!adev->mc.srbm_soft_reset)
                return 0;
 
        gmc_v8_0_mc_stop(adev, &adev->mc.save);
@@ -1145,7 +1144,7 @@ static int gmc_v8_0_soft_reset(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 srbm_soft_reset;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang)
+       if (!adev->mc.srbm_soft_reset)
                return 0;
        srbm_soft_reset = adev->mc.srbm_soft_reset;
 
@@ -1175,7 +1174,7 @@ static int gmc_v8_0_post_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang)
+       if (!adev->mc.srbm_soft_reset)
                return 0;
 
        gmc_v8_0_mc_resume(adev, &adev->mc.save);
index f325fd86430b9e3d565f28fd5b11ace2c2a76667..a9d10941fb53d9ab2290fd6bdbe3351d91b0acaa 100644 (file)
@@ -1268,7 +1268,7 @@ static int sdma_v3_0_wait_for_idle(void *handle)
        return -ETIMEDOUT;
 }
 
-static int sdma_v3_0_check_soft_reset(void *handle)
+static bool sdma_v3_0_check_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 srbm_soft_reset = 0;
@@ -1281,14 +1281,12 @@ static int sdma_v3_0_check_soft_reset(void *handle)
        }
 
        if (srbm_soft_reset) {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_SDMA].hang = true;
                adev->sdma.srbm_soft_reset = srbm_soft_reset;
+               return true;
        } else {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_SDMA].hang = false;
                adev->sdma.srbm_soft_reset = 0;
+               return false;
        }
-
-       return 0;
 }
 
 static int sdma_v3_0_pre_soft_reset(void *handle)
@@ -1296,7 +1294,7 @@ static int sdma_v3_0_pre_soft_reset(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 srbm_soft_reset = 0;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_SDMA].hang)
+       if (!adev->sdma.srbm_soft_reset)
                return 0;
 
        srbm_soft_reset = adev->sdma.srbm_soft_reset;
@@ -1315,7 +1313,7 @@ static int sdma_v3_0_post_soft_reset(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 srbm_soft_reset = 0;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_SDMA].hang)
+       if (!adev->sdma.srbm_soft_reset)
                return 0;
 
        srbm_soft_reset = adev->sdma.srbm_soft_reset;
@@ -1335,7 +1333,7 @@ static int sdma_v3_0_soft_reset(void *handle)
        u32 srbm_soft_reset = 0;
        u32 tmp;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_SDMA].hang)
+       if (!adev->sdma.srbm_soft_reset)
                return 0;
 
        srbm_soft_reset = adev->sdma.srbm_soft_reset;
index 8bd08925b370b753fbe217031c1e29e5b3b4426e..3de7bca5854b1b06f20077d177c5476511d5e74b 100644 (file)
@@ -3499,6 +3499,12 @@ static void si_apply_state_adjust_rules(struct amdgpu_device *adev,
                max_sclk = 75000;
                max_mclk = 80000;
        }
+       /* Limit clocks for some HD8600 parts */
+       if (adev->pdev->device == 0x6660 &&
+           adev->pdev->revision == 0x83) {
+               max_sclk = 75000;
+               max_mclk = 80000;
+       }
 
        if (rps->vce_active) {
                rps->evclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].evclk;
index d127d59f953a8ded522884fa7f9eba77e648db2d..b4ea229bb4498ff1f84209dec7f211198ed788ed 100644 (file)
@@ -373,7 +373,7 @@ static int tonga_ih_wait_for_idle(void *handle)
        return -ETIMEDOUT;
 }
 
-static int tonga_ih_check_soft_reset(void *handle)
+static bool tonga_ih_check_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 srbm_soft_reset = 0;
@@ -384,21 +384,19 @@ static int tonga_ih_check_soft_reset(void *handle)
                                                SOFT_RESET_IH, 1);
 
        if (srbm_soft_reset) {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_IH].hang = true;
                adev->irq.srbm_soft_reset = srbm_soft_reset;
+               return true;
        } else {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_IH].hang = false;
                adev->irq.srbm_soft_reset = 0;
+               return false;
        }
-
-       return 0;
 }
 
 static int tonga_ih_pre_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_IH].hang)
+       if (!adev->irq.srbm_soft_reset)
                return 0;
 
        return tonga_ih_hw_fini(adev);
@@ -408,7 +406,7 @@ static int tonga_ih_post_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_IH].hang)
+       if (!adev->irq.srbm_soft_reset)
                return 0;
 
        return tonga_ih_hw_init(adev);
@@ -419,7 +417,7 @@ static int tonga_ih_soft_reset(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 srbm_soft_reset;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_IH].hang)
+       if (!adev->irq.srbm_soft_reset)
                return 0;
        srbm_soft_reset = adev->irq.srbm_soft_reset;
 
index e0fd9f21ed9585ce37c310605f2ea56524fa60bc..ab3df6d756562ee33b97d2c48aaf6f7bfadc6f2a 100644 (file)
@@ -770,7 +770,7 @@ static int uvd_v6_0_wait_for_idle(void *handle)
 }
 
 #define AMDGPU_UVD_STATUS_BUSY_MASK    0xfd
-static int uvd_v6_0_check_soft_reset(void *handle)
+static bool uvd_v6_0_check_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 srbm_soft_reset = 0;
@@ -782,19 +782,19 @@ static int uvd_v6_0_check_soft_reset(void *handle)
                srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_UVD, 1);
 
        if (srbm_soft_reset) {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_UVD].hang = true;
                adev->uvd.srbm_soft_reset = srbm_soft_reset;
+               return true;
        } else {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_UVD].hang = false;
                adev->uvd.srbm_soft_reset = 0;
+               return false;
        }
-       return 0;
 }
+
 static int uvd_v6_0_pre_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_UVD].hang)
+       if (!adev->uvd.srbm_soft_reset)
                return 0;
 
        uvd_v6_0_stop(adev);
@@ -806,7 +806,7 @@ static int uvd_v6_0_soft_reset(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 srbm_soft_reset;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_UVD].hang)
+       if (!adev->uvd.srbm_soft_reset)
                return 0;
        srbm_soft_reset = adev->uvd.srbm_soft_reset;
 
@@ -836,7 +836,7 @@ static int uvd_v6_0_post_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_UVD].hang)
+       if (!adev->uvd.srbm_soft_reset)
                return 0;
 
        mdelay(5);
index 3f6db4ec0102d0f54d2b5cba6c33a76b9d64ad46..8533269ec1606f1ed26714d9655a5eae20b49421 100644 (file)
@@ -561,7 +561,7 @@ static int vce_v3_0_wait_for_idle(void *handle)
 #define  AMDGPU_VCE_STATUS_BUSY_MASK (VCE_STATUS_VCPU_REPORT_AUTO_BUSY_MASK | \
                                      VCE_STATUS_VCPU_REPORT_RB0_BUSY_MASK)
 
-static int vce_v3_0_check_soft_reset(void *handle)
+static bool vce_v3_0_check_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 srbm_soft_reset = 0;
@@ -591,16 +591,15 @@ static int vce_v3_0_check_soft_reset(void *handle)
                srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE1, 1);
        }
        WREG32_FIELD(GRBM_GFX_INDEX, INSTANCE_INDEX, 0);
+       mutex_unlock(&adev->grbm_idx_mutex);
 
        if (srbm_soft_reset) {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_VCE].hang = true;
                adev->vce.srbm_soft_reset = srbm_soft_reset;
+               return true;
        } else {
-               adev->ip_block_status[AMD_IP_BLOCK_TYPE_VCE].hang = false;
                adev->vce.srbm_soft_reset = 0;
+               return false;
        }
-       mutex_unlock(&adev->grbm_idx_mutex);
-       return 0;
 }
 
 static int vce_v3_0_soft_reset(void *handle)
@@ -608,7 +607,7 @@ static int vce_v3_0_soft_reset(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 srbm_soft_reset;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_VCE].hang)
+       if (!adev->vce.srbm_soft_reset)
                return 0;
        srbm_soft_reset = adev->vce.srbm_soft_reset;
 
@@ -638,7 +637,7 @@ static int vce_v3_0_pre_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_VCE].hang)
+       if (!adev->vce.srbm_soft_reset)
                return 0;
 
        mdelay(5);
@@ -651,7 +650,7 @@ static int vce_v3_0_post_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_VCE].hang)
+       if (!adev->vce.srbm_soft_reset)
                return 0;
 
        mdelay(5);
index c934b78c9e2f056b9ba20ec9f2b182cfa1910e74..bec8125bceb0d2555b49b8badaafbf4494ac47f7 100644 (file)
@@ -165,7 +165,7 @@ struct amd_ip_funcs {
        /* poll for idle */
        int (*wait_for_idle)(void *handle);
        /* check soft reset the IP block */
-       int (*check_soft_reset)(void *handle);
+       bool (*check_soft_reset)(void *handle);
        /* pre soft reset the IP block */
        int (*pre_soft_reset)(void *handle);
        /* soft reset the IP block */
index 92b1178438755ab4e981dfa14741f62a3353da9f..8cee4e0f9fde60c736b56344b378c67c2107d867 100644 (file)
@@ -49,6 +49,7 @@ static const pem_event_action * const uninitialize_event[] = {
        uninitialize_display_phy_access_tasks,
        disable_gfx_voltage_island_power_gating_tasks,
        disable_gfx_clock_gating_tasks,
+       uninitialize_thermal_controller_tasks,
        set_boot_state_tasks,
        adjust_power_state_tasks,
        disable_dynamic_state_management_tasks,
index 7e4fcbbbe08652735c6796a2cbfd62d122434e69..960424913496d671d70fa220600fab6874dbd67d 100644 (file)
@@ -1785,6 +1785,21 @@ static int cz_get_max_high_clocks(struct pp_hwmgr *hwmgr, struct amd_pp_simple_c
        return 0;
 }
 
+static int cz_thermal_get_temperature(struct pp_hwmgr *hwmgr)
+{
+       int actual_temp = 0;
+       uint32_t val = cgs_read_ind_register(hwmgr->device,
+                                            CGS_IND_REG__SMC, ixTHM_TCON_CUR_TMP);
+       uint32_t temp = PHM_GET_FIELD(val, THM_TCON_CUR_TMP, CUR_TEMP);
+
+       if (PHM_GET_FIELD(val, THM_TCON_CUR_TMP, CUR_TEMP_RANGE_SEL))
+               actual_temp = ((temp / 8) - 49) * PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+       else
+               actual_temp = (temp / 8) * PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+       return actual_temp;
+}
+
 static int cz_read_sensor(struct pp_hwmgr *hwmgr, int idx, int32_t *value)
 {
        struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend);
@@ -1881,6 +1896,9 @@ static int cz_read_sensor(struct pp_hwmgr *hwmgr, int idx, int32_t *value)
        case AMDGPU_PP_SENSOR_VCE_POWER:
                *value = cz_hwmgr->vce_power_gated ? 0 : 1;
                return 0;
+       case AMDGPU_PP_SENSOR_GPU_TEMP:
+               *value = cz_thermal_get_temperature(hwmgr);
+               return 0;
        default:
                return -EINVAL;
        }
index 508245d49d3394055835683f3067e021f6d482d0..609996c84ad5ae8681bc8fa4db53f7e4bcb548e2 100644 (file)
@@ -1030,20 +1030,19 @@ static int smu7_disable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
        struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
 
        /* disable SCLK dpm */
-       if (!data->sclk_dpm_key_disabled)
-               PP_ASSERT_WITH_CODE(
-                               (smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_DPM_Disable) == 0),
-                               "Failed to disable SCLK DPM!",
-                               return -EINVAL);
+       if (!data->sclk_dpm_key_disabled) {
+               PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+                               "Trying to disable SCLK DPM when DPM is disabled",
+                               return 0);
+               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_DPM_Disable);
+       }
 
        /* disable MCLK dpm */
        if (!data->mclk_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE(
-                               (smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_MCLKDPM_Disable) == 0),
-                               "Failed to disable MCLK DPM!",
-                               return -EINVAL);
+               PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+                               "Trying to disable MCLK DPM when DPM is disabled",
+                               return 0);
+               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_MCLKDPM_Disable);
        }
 
        return 0;
@@ -1069,10 +1068,13 @@ static int smu7_stop_dpm(struct pp_hwmgr *hwmgr)
                                return -EINVAL);
        }
 
-       if (smu7_disable_sclk_mclk_dpm(hwmgr)) {
-               printk(KERN_ERR "Failed to disable Sclk DPM and Mclk DPM!");
-               return -EINVAL;
-       }
+       smu7_disable_sclk_mclk_dpm(hwmgr);
+
+       PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+                       "Trying to disable voltage DPM when DPM is disabled",
+                       return 0);
+
+       smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Voltage_Cntl_Disable);
 
        return 0;
 }
@@ -1226,7 +1228,7 @@ int smu7_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
        PP_ASSERT_WITH_CODE((0 == tmp_result),
                        "Failed to enable VR hot GPIO interrupt!", result = tmp_result);
 
-       smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)PPSMC_HasDisplay);
+       smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)PPSMC_NoDisplay);
 
        tmp_result = smu7_enable_sclk_control(hwmgr);
        PP_ASSERT_WITH_CODE((0 == tmp_result),
@@ -1306,6 +1308,12 @@ int smu7_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
        PP_ASSERT_WITH_CODE((tmp_result == 0),
                        "Failed to disable thermal auto throttle!", result = tmp_result);
 
+       if (1 == PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, FEATURE_STATUS, AVS_ON)) {
+               PP_ASSERT_WITH_CODE((0 == smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_DisableAvfs)),
+                                       "Failed to disable AVFS!",
+                                       return -EINVAL);
+       }
+
        tmp_result = smu7_stop_dpm(hwmgr);
        PP_ASSERT_WITH_CODE((tmp_result == 0),
                        "Failed to stop DPM!", result = tmp_result);
@@ -1452,8 +1460,10 @@ static int smu7_get_evv_voltages(struct pp_hwmgr *hwmgr)
        struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table = NULL;
 
 
-       if (table_info != NULL)
-               sclk_table = table_info->vdd_dep_on_sclk;
+       if (table_info == NULL)
+               return -EINVAL;
+
+       sclk_table = table_info->vdd_dep_on_sclk;
 
        for (i = 0; i < SMU7_MAX_LEAKAGE_COUNT; i++) {
                vv_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
@@ -3802,13 +3812,15 @@ static inline bool smu7_are_power_levels_equal(const struct smu7_performance_lev
 
 int smu7_check_states_equal(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *pstate1, const struct pp_hw_power_state *pstate2, bool *equal)
 {
-       const struct smu7_power_state *psa = cast_const_phw_smu7_power_state(pstate1);
-       const struct smu7_power_state *psb = cast_const_phw_smu7_power_state(pstate2);
+       const struct smu7_power_state *psa;
+       const struct smu7_power_state *psb;
        int i;
 
        if (pstate1 == NULL || pstate2 == NULL || equal == NULL)
                return -EINVAL;
 
+       psa = cast_const_phw_smu7_power_state(pstate1);
+       psb = cast_const_phw_smu7_power_state(pstate2);
        /* If the two states don't even have the same number of performance levels they cannot be the same state. */
        if (psa->performance_level_count != psb->performance_level_count) {
                *equal = false;
@@ -4324,6 +4336,7 @@ static const struct pp_hwmgr_func smu7_hwmgr_funcs = {
        .set_mclk_od = smu7_set_mclk_od,
        .get_clock_by_type = smu7_get_clock_by_type,
        .read_sensor = smu7_read_sensor,
+       .dynamic_state_management_disable = smu7_disable_dpm_tasks,
 };
 
 uint8_t smu7_get_sleep_divider_id_from_clock(uint32_t clock,
index eda802bc63c888ead2082e4362768ba763d1d83f..8c889caba420dc2d55ec9340d46ab0a92d468514 100644 (file)
@@ -2458,7 +2458,7 @@ static int iceland_set_mc_special_registers(struct pp_hwmgr *hwmgr,
                        PP_ASSERT_WITH_CODE((j <= SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
                                "Invalid VramInfo table.", return -EINVAL);
 
-                       if (!data->is_memory_gddr5) {
+                       if (!data->is_memory_gddr5 && j < SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE) {
                                table->mc_reg_address[j].s1 = mmMC_PMG_AUTO_CMD;
                                table->mc_reg_address[j].s0 = mmMC_PMG_AUTO_CMD;
                                for (k = 0; k < table->num_entries; k++) {
index 1df2d33d0b40ed43e0cbd4623e014851b34ce581..ffb2ab389d1d14863ed1e69c17419d560910afe5 100644 (file)
@@ -54,9 +54,6 @@ int drm_name_info(struct seq_file *m, void *data)
 
        mutex_lock(&dev->master_mutex);
        master = dev->master;
-       if (!master)
-               goto out_unlock;
-
        seq_printf(m, "%s", dev->driver->name);
        if (dev->dev)
                seq_printf(m, " dev=%s", dev_name(dev->dev));
@@ -65,7 +62,6 @@ int drm_name_info(struct seq_file *m, void *data)
        if (dev->unique)
                seq_printf(m, " unique=%s", dev->unique);
        seq_printf(m, "\n");
-out_unlock:
        mutex_unlock(&dev->master_mutex);
 
        return 0;
index 5ce3603e6eacb1aab7c59ed66399f743d5346990..0370b842d9cc20c2fdae37406c82a9546106e69b 100644 (file)
@@ -748,19 +748,22 @@ static struct page **etnaviv_gem_userptr_do_get_pages(
        int ret = 0, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT;
        struct page **pvec;
        uintptr_t ptr;
+       unsigned int flags = 0;
 
        pvec = drm_malloc_ab(npages, sizeof(struct page *));
        if (!pvec)
                return ERR_PTR(-ENOMEM);
 
+       if (!etnaviv_obj->userptr.ro)
+               flags |= FOLL_WRITE;
+
        pinned = 0;
        ptr = etnaviv_obj->userptr.ptr;
 
        down_read(&mm->mmap_sem);
        while (pinned < npages) {
                ret = get_user_pages_remote(task, mm, ptr, npages - pinned,
-                                           !etnaviv_obj->userptr.ro, 0,
-                                           pvec + pinned, NULL);
+                                           flags, pvec + pinned, NULL);
                if (ret < 0)
                        break;
 
index aa92decf4233df4a4f06252a85c60ac3a7cd7a37..fbd13fabdf2daf93aeb79cb976c47e467fae6971 100644 (file)
@@ -488,7 +488,8 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
                goto err_free;
        }
 
-       ret = get_vaddr_frames(start, npages, true, true, g2d_userptr->vec);
+       ret = get_vaddr_frames(start, npages, FOLL_FORCE | FOLL_WRITE,
+               g2d_userptr->vec);
        if (ret != npages) {
                DRM_ERROR("failed to get user pages from userptr.\n");
                if (ret < 0)
index e537930c64b53d5a18ebbf7fcb79be68f0114acc..c6f780f5abc9cf3776edcfa67a6fa08a6b53239a 100644 (file)
@@ -508,6 +508,10 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
        pvec = drm_malloc_gfp(npages, sizeof(struct page *), GFP_TEMPORARY);
        if (pvec != NULL) {
                struct mm_struct *mm = obj->userptr.mm->mm;
+               unsigned int flags = 0;
+
+               if (!obj->userptr.read_only)
+                       flags |= FOLL_WRITE;
 
                ret = -EFAULT;
                if (atomic_inc_not_zero(&mm->mm_users)) {
@@ -517,7 +521,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
                                        (work->task, mm,
                                         obj->userptr.ptr + pinned * PAGE_SIZE,
                                         npages - pinned,
-                                        !obj->userptr.read_only, 0,
+                                        flags,
                                         pvec + pinned, NULL);
                                if (ret < 0)
                                        break;
index 6a4b020dd0b45aa504bcb7acd68c49a0ddfc9328..5a26eb4545aae4afea2dc83d6b5e615b1ddcfff3 100644 (file)
@@ -156,19 +156,20 @@ u32 r600_dpm_get_vblank_time(struct radeon_device *rdev)
        struct drm_device *dev = rdev->ddev;
        struct drm_crtc *crtc;
        struct radeon_crtc *radeon_crtc;
-       u32 line_time_us, vblank_lines;
+       u32 vblank_in_pixels;
        u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */
 
        if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
                list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                        radeon_crtc = to_radeon_crtc(crtc);
                        if (crtc->enabled && radeon_crtc->enabled && radeon_crtc->hw_mode.clock) {
-                               line_time_us = (radeon_crtc->hw_mode.crtc_htotal * 1000) /
-                                       radeon_crtc->hw_mode.clock;
-                               vblank_lines = radeon_crtc->hw_mode.crtc_vblank_end -
-                                       radeon_crtc->hw_mode.crtc_vdisplay +
-                                       (radeon_crtc->v_border * 2);
-                               vblank_time_us = vblank_lines * line_time_us;
+                               vblank_in_pixels =
+                                       radeon_crtc->hw_mode.crtc_htotal *
+                                       (radeon_crtc->hw_mode.crtc_vblank_end -
+                                        radeon_crtc->hw_mode.crtc_vdisplay +
+                                        (radeon_crtc->v_border * 2));
+
+                               vblank_time_us = vblank_in_pixels * 1000 / radeon_crtc->hw_mode.clock;
                                break;
                        }
                }
index 50e96d2c593dafe05c0e0205288c7f2b7c6a71d9..e18839d52e3e9036c53846b4cb67e5670a52ba7a 100644 (file)
@@ -927,6 +927,16 @@ radeon_lvds_detect(struct drm_connector *connector, bool force)
        return ret;
 }
 
+static void radeon_connector_unregister(struct drm_connector *connector)
+{
+       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+       if (radeon_connector->ddc_bus->has_aux) {
+               drm_dp_aux_unregister(&radeon_connector->ddc_bus->aux);
+               radeon_connector->ddc_bus->has_aux = false;
+       }
+}
+
 static void radeon_connector_destroy(struct drm_connector *connector)
 {
        struct radeon_connector *radeon_connector = to_radeon_connector(connector);
@@ -984,6 +994,7 @@ static const struct drm_connector_funcs radeon_lvds_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
        .detect = radeon_lvds_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
+       .early_unregister = radeon_connector_unregister,
        .destroy = radeon_connector_destroy,
        .set_property = radeon_lvds_set_property,
 };
@@ -1111,6 +1122,7 @@ static const struct drm_connector_funcs radeon_vga_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
        .detect = radeon_vga_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
+       .early_unregister = radeon_connector_unregister,
        .destroy = radeon_connector_destroy,
        .set_property = radeon_connector_set_property,
 };
@@ -1188,6 +1200,7 @@ static const struct drm_connector_funcs radeon_tv_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
        .detect = radeon_tv_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
+       .early_unregister = radeon_connector_unregister,
        .destroy = radeon_connector_destroy,
        .set_property = radeon_connector_set_property,
 };
@@ -1519,6 +1532,7 @@ static const struct drm_connector_funcs radeon_dvi_connector_funcs = {
        .detect = radeon_dvi_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = radeon_connector_set_property,
+       .early_unregister = radeon_connector_unregister,
        .destroy = radeon_connector_destroy,
        .force = radeon_dvi_force,
 };
@@ -1832,6 +1846,7 @@ static const struct drm_connector_funcs radeon_dp_connector_funcs = {
        .detect = radeon_dp_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = radeon_connector_set_property,
+       .early_unregister = radeon_connector_unregister,
        .destroy = radeon_connector_destroy,
        .force = radeon_dvi_force,
 };
@@ -1841,6 +1856,7 @@ static const struct drm_connector_funcs radeon_edp_connector_funcs = {
        .detect = radeon_dp_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = radeon_lvds_set_property,
+       .early_unregister = radeon_connector_unregister,
        .destroy = radeon_connector_destroy,
        .force = radeon_dvi_force,
 };
@@ -1850,6 +1866,7 @@ static const struct drm_connector_funcs radeon_lvds_bridge_connector_funcs = {
        .detect = radeon_dp_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = radeon_lvds_set_property,
+       .early_unregister = radeon_connector_unregister,
        .destroy = radeon_connector_destroy,
        .force = radeon_dvi_force,
 };
index b8ab30a7dd6d2f2d215415751539c652a3b1ed55..cdb8cb568c15310589039b2f0c56faf5cbf11b9c 100644 (file)
@@ -1675,20 +1675,20 @@ int radeon_modeset_init(struct radeon_device *rdev)
 
 void radeon_modeset_fini(struct radeon_device *rdev)
 {
-       radeon_fbdev_fini(rdev);
-       kfree(rdev->mode_info.bios_hardcoded_edid);
-
-       /* free i2c buses */
-       radeon_i2c_fini(rdev);
-
        if (rdev->mode_info.mode_config_initialized) {
-               radeon_afmt_fini(rdev);
                drm_kms_helper_poll_fini(rdev->ddev);
                radeon_hpd_fini(rdev);
                drm_crtc_force_disable_all(rdev->ddev);
+               radeon_fbdev_fini(rdev);
+               radeon_afmt_fini(rdev);
                drm_mode_config_cleanup(rdev->ddev);
                rdev->mode_info.mode_config_initialized = false;
        }
+
+       kfree(rdev->mode_info.bios_hardcoded_edid);
+
+       /* free i2c buses */
+       radeon_i2c_fini(rdev);
 }
 
 static bool is_hdtv_mode(const struct drm_display_mode *mode)
index 91c8f433956605e1f5106b69df1af9123e3bd2c5..00ea0002b539b9e9b5b0a063f62deb3b7638fd56 100644 (file)
  *   2.45.0 - Allow setting shader registers using DMA/COPY packet3 on SI
  *   2.46.0 - Add PFP_SYNC_ME support on evergreen
  *   2.47.0 - Add UVD_NO_OP register support
+ *   2.48.0 - TA_CS_BC_BASE_ADDR allowed on SI
  */
 #define KMS_DRIVER_MAJOR       2
-#define KMS_DRIVER_MINOR       47
+#define KMS_DRIVER_MINOR       48
 #define KMS_DRIVER_PATCHLEVEL  0
 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
 int radeon_driver_unload_kms(struct drm_device *dev);
index 021aa005623f804be130179398a617291032c92b..29f7817af821babcfe36e148c6f5a0fb8065e035 100644 (file)
@@ -982,9 +982,8 @@ void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
 {
        if (!i2c)
                return;
+       WARN_ON(i2c->has_aux);
        i2c_del_adapter(&i2c->adapter);
-       if (i2c->has_aux)
-               drm_dp_aux_unregister(&i2c->aux);
        kfree(i2c);
 }
 
index 455268214b893eac36e8bbd65d5e2b18d2735483..3de5e6e216628233ef1ba997bfcab6ae09d24621 100644 (file)
@@ -566,7 +566,8 @@ static int radeon_ttm_tt_pin_userptr(struct ttm_tt *ttm)
                uint64_t userptr = gtt->userptr + pinned * PAGE_SIZE;
                struct page **pages = ttm->pages + pinned;
 
-               r = get_user_pages(userptr, num_pages, write, 0, pages, NULL);
+               r = get_user_pages(userptr, num_pages, write ? FOLL_WRITE : 0,
+                                  pages, NULL);
                if (r < 0)
                        goto release_pages;
 
index 7ee9aafbdf744bc0a97f358767e5b2178f24c41a..e402be8821c45271a276f721f1eb9c94fcf4afb5 100644 (file)
@@ -4431,6 +4431,7 @@ static bool si_vm_reg_valid(u32 reg)
        case SPI_CONFIG_CNTL:
        case SPI_CONFIG_CNTL_1:
        case TA_CNTL_AUX:
+       case TA_CS_BC_BASE_ADDR:
                return true;
        default:
                DRM_ERROR("Invalid register 0x%x in CS\n", reg);
index eb220eecba789aaf40ced21852a8d98441e55a3b..65a911ddd509d29d2a5d460ada79674e947a96f3 100644 (file)
 #define        SPI_LB_CU_MASK                                  0x9354
 
 #define        TA_CNTL_AUX                                     0x9508
+#define        TA_CS_BC_BASE_ADDR                              0x950C
 
 #define CC_RB_BACKEND_DISABLE                          0x98F4
 #define                BACKEND_DISABLE(x)                      ((x) << 16)
index 7e2a12c4fed2a49bb5f35714e8ef42f24cf8f7d1..1a3ad769f8c85bc0506741d105669dd66013b533 100644 (file)
@@ -241,8 +241,8 @@ via_lock_all_dma_pages(drm_via_sg_info_t *vsg,  drm_via_dmablit_t *xfer)
        down_read(&current->mm->mmap_sem);
        ret = get_user_pages((unsigned long)xfer->mem_addr,
                             vsg->num_pages,
-                            (vsg->direction == DMA_FROM_DEVICE),
-                            0, vsg->pages, NULL);
+                            (vsg->direction == DMA_FROM_DEVICE) ? FOLL_WRITE : 0,
+                            vsg->pages, NULL);
 
        up_read(&current->mm->mmap_sem);
        if (ret != vsg->num_pages) {
index 8fd4bf77f264940ec04631252062e06aafe148c8..818ea7d935333046adc5036d141ffa6cf6be5c40 100644 (file)
@@ -234,58 +234,6 @@ static __u8 pid0011_rdesc_fixed[] = {
        0xC0                /*  End Collection                  */
 };
 
-static __u8 pid0006_rdesc_fixed[] = {
-       0x05, 0x01,        /* Usage Page (Generic Desktop)      */
-       0x09, 0x04,        /* Usage (Joystick)                  */
-       0xA1, 0x01,        /* Collection (Application)          */
-       0xA1, 0x02,        /*   Collection (Logical)            */
-       0x75, 0x08,        /*     Report Size (8)               */
-       0x95, 0x05,        /*     Report Count (5)              */
-       0x15, 0x00,        /*     Logical Minimum (0)           */
-       0x26, 0xFF, 0x00,  /*     Logical Maximum (255)         */
-       0x35, 0x00,        /*     Physical Minimum (0)          */
-       0x46, 0xFF, 0x00,  /*     Physical Maximum (255)        */
-       0x09, 0x30,        /*     Usage (X)                     */
-       0x09, 0x33,        /*     Usage (Ry)                    */
-       0x09, 0x32,        /*     Usage (Z)                     */
-       0x09, 0x31,        /*     Usage (Y)                     */
-       0x09, 0x34,        /*     Usage (Ry)                    */
-       0x81, 0x02,        /*     Input (Variable)              */
-       0x75, 0x04,        /*     Report Size (4)               */
-       0x95, 0x01,        /*     Report Count (1)              */
-       0x25, 0x07,        /*     Logical Maximum (7)           */
-       0x46, 0x3B, 0x01,  /*     Physical Maximum (315)        */
-       0x65, 0x14,        /*     Unit (Centimeter)             */
-       0x09, 0x39,        /*     Usage (Hat switch)            */
-       0x81, 0x42,        /*     Input (Variable)              */
-       0x65, 0x00,        /*     Unit (None)                   */
-       0x75, 0x01,        /*     Report Size (1)               */
-       0x95, 0x0C,        /*     Report Count (12)             */
-       0x25, 0x01,        /*     Logical Maximum (1)           */
-       0x45, 0x01,        /*     Physical Maximum (1)          */
-       0x05, 0x09,        /*     Usage Page (Button)           */
-       0x19, 0x01,        /*     Usage Minimum (0x01)          */
-       0x29, 0x0C,        /*     Usage Maximum (0x0C)          */
-       0x81, 0x02,        /*     Input (Variable)              */
-       0x06, 0x00, 0xFF,  /*     Usage Page (Vendor Defined)   */
-       0x75, 0x01,        /*     Report Size (1)               */
-       0x95, 0x08,        /*     Report Count (8)              */
-       0x25, 0x01,        /*     Logical Maximum (1)           */
-       0x45, 0x01,        /*     Physical Maximum (1)          */
-       0x09, 0x01,        /*     Usage (0x01)                  */
-       0x81, 0x02,        /*     Input (Variable)              */
-       0xC0,              /*   End Collection                  */
-       0xA1, 0x02,        /*   Collection (Logical)            */
-       0x75, 0x08,        /*     Report Size (8)               */
-       0x95, 0x07,        /*     Report Count (7)              */
-       0x46, 0xFF, 0x00,  /*     Physical Maximum (255)        */
-       0x26, 0xFF, 0x00,  /*     Logical Maximum (255)         */
-       0x09, 0x02,        /*     Usage (0x02)                  */
-       0x91, 0x02,        /*     Output (Variable)             */
-       0xC0,              /*   End Collection                  */
-       0xC0               /* End Collection                    */
-};
-
 static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                                unsigned int *rsize)
 {
@@ -296,16 +244,34 @@ static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                        *rsize = sizeof(pid0011_rdesc_fixed);
                }
                break;
-       case 0x0006:
-               if (*rsize == sizeof(pid0006_rdesc_fixed)) {
-                       rdesc = pid0006_rdesc_fixed;
-                       *rsize = sizeof(pid0006_rdesc_fixed);
-               }
-               break;
        }
        return rdesc;
 }
 
+#define map_abs(c)      hid_map_usage(hi, usage, bit, max, EV_ABS, (c))
+#define map_rel(c)      hid_map_usage(hi, usage, bit, max, EV_REL, (c))
+
+static int dr_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+                           struct hid_field *field, struct hid_usage *usage,
+                           unsigned long **bit, int *max)
+{
+       switch (usage->hid) {
+       /*
+        * revert to the old hid-input behavior where axes
+        * can be randomly assigned when hid->usage is reused.
+        */
+       case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
+       case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
+               if (field->flags & HID_MAIN_ITEM_RELATIVE)
+                       map_rel(usage->hid & 0xf);
+               else
+                       map_abs(usage->hid & 0xf);
+               return 1;
+       }
+
+       return 0;
+}
+
 static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        int ret;
@@ -352,6 +318,7 @@ static struct hid_driver dr_driver = {
        .id_table = dr_devices,
        .report_fixup = dr_report_fixup,
        .probe = dr_probe,
+       .input_mapping = dr_input_mapping,
 };
 module_hid_driver(dr_driver);
 
index cd59c79eebdd2bd896c1ff19d687f25686d7fb35..6cfb5cacc2533b3f5a21c488ff7257c44d3b43df 100644 (file)
@@ -64,6 +64,9 @@
 #define USB_VENDOR_ID_AKAI             0x2011
 #define USB_DEVICE_ID_AKAI_MPKMINI2    0x0715
 
+#define USB_VENDOR_ID_AKAI_09E8                0x09E8
+#define USB_DEVICE_ID_AKAI_09E8_MIDIMIX        0x0031
+
 #define USB_VENDOR_ID_ALCOR            0x058f
 #define USB_DEVICE_ID_ALCOR_USBRS232   0x9720
 
index d8d55f37b4f5604e2d10672513267c483726f05e..d3e1ab162f7c6ade6e2a83053ec1d719f1a08daa 100644 (file)
@@ -100,6 +100,7 @@ struct hidled_device {
        const struct hidled_config *config;
        struct hid_device       *hdev;
        struct hidled_rgb       *rgb;
+       u8                      *buf;
        struct mutex            lock;
 };
 
@@ -118,13 +119,19 @@ static int hidled_send(struct hidled_device *ldev, __u8 *buf)
 
        mutex_lock(&ldev->lock);
 
+       /*
+        * buffer provided to hid_hw_raw_request must not be on the stack
+        * and must not be part of a data structure
+        */
+       memcpy(ldev->buf, buf, ldev->config->report_size);
+
        if (ldev->config->report_type == RAW_REQUEST)
-               ret = hid_hw_raw_request(ldev->hdev, buf[0], buf,
+               ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf,
                                         ldev->config->report_size,
                                         HID_FEATURE_REPORT,
                                         HID_REQ_SET_REPORT);
        else if (ldev->config->report_type == OUTPUT_REPORT)
-               ret = hid_hw_output_report(ldev->hdev, buf,
+               ret = hid_hw_output_report(ldev->hdev, ldev->buf,
                                           ldev->config->report_size);
        else
                ret = -EINVAL;
@@ -147,17 +154,21 @@ static int hidled_recv(struct hidled_device *ldev, __u8 *buf)
 
        mutex_lock(&ldev->lock);
 
-       ret = hid_hw_raw_request(ldev->hdev, buf[0], buf,
+       memcpy(ldev->buf, buf, ldev->config->report_size);
+
+       ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf,
                                 ldev->config->report_size,
                                 HID_FEATURE_REPORT,
                                 HID_REQ_SET_REPORT);
        if (ret < 0)
                goto err;
 
-       ret = hid_hw_raw_request(ldev->hdev, buf[0], buf,
+       ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf,
                                 ldev->config->report_size,
                                 HID_FEATURE_REPORT,
                                 HID_REQ_GET_REPORT);
+
+       memcpy(buf, ldev->buf, ldev->config->report_size);
 err:
        mutex_unlock(&ldev->lock);
 
@@ -447,6 +458,10 @@ static int hidled_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (!ldev)
                return -ENOMEM;
 
+       ldev->buf = devm_kmalloc(&hdev->dev, MAX_REPORT_SIZE, GFP_KERNEL);
+       if (!ldev->buf)
+               return -ENOMEM;
+
        ret = hid_parse(hdev);
        if (ret)
                return ret;
index 0a0eca5da47d0274cca10822e3b8d6aded20b6c7..354d49ea36dd9d434452cff07e2b0fb15356035e 100644 (file)
@@ -56,6 +56,7 @@ static const struct hid_blacklist {
 
        { USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_AKAI, USB_DEVICE_ID_AKAI_MPKMINI2, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_AKAI_09E8, USB_DEVICE_ID_AKAI_09E8_MIDIMIX, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
index 98fffa3a09f7fe5e716415c79963b7ac15587b9c..5ab67219f71e64a95c926e20b4c45cb1edce68bf 100644 (file)
@@ -1680,7 +1680,7 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
 
 static void of_i2c_register_devices(struct i2c_adapter *adap)
 {
-       struct device_node *node;
+       struct device_node *bus, *node;
 
        /* Only register child devices if the adapter has a node pointer set */
        if (!adap->dev.of_node)
@@ -1688,11 +1688,17 @@ static void of_i2c_register_devices(struct i2c_adapter *adap)
 
        dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
 
-       for_each_available_child_of_node(adap->dev.of_node, node) {
+       bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus");
+       if (!bus)
+               bus = of_node_get(adap->dev.of_node);
+
+       for_each_available_child_of_node(bus, node) {
                if (of_node_test_and_set_flag(node, OF_POPULATED))
                        continue;
                of_i2c_register_device(adap, node);
        }
+
+       of_node_put(bus);
 }
 
 static int of_dev_node_match(struct device *dev, void *data)
index 19a418a1b631250f3f652c256a75bea30e76f3a3..fb3fb89640e59c3d396e133aab2b47ddd9b6ab15 100644 (file)
@@ -89,4 +89,6 @@ source "drivers/infiniband/sw/rxe/Kconfig"
 
 source "drivers/infiniband/hw/hfi1/Kconfig"
 
+source "drivers/infiniband/hw/qedr/Kconfig"
+
 endif # INFINIBAND
index c68746ce6624cdd7f0fcc9ecd4db851e45c4b497..224ad274ea0b8a73e30304e8e5bc9f935c8b98e5 100644 (file)
@@ -94,6 +94,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
        unsigned long dma_attrs = 0;
        struct scatterlist *sg, *sg_list_start;
        int need_release = 0;
+       unsigned int gup_flags = FOLL_WRITE;
 
        if (dmasync)
                dma_attrs |= DMA_ATTR_WRITE_BARRIER;
@@ -183,6 +184,9 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
        if (ret)
                goto out;
 
+       if (!umem->writable)
+               gup_flags |= FOLL_FORCE;
+
        need_release = 1;
        sg_list_start = umem->sg_head.sgl;
 
@@ -190,7 +194,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
                ret = get_user_pages(cur_base,
                                     min_t(unsigned long, npages,
                                           PAGE_SIZE / sizeof (struct page *)),
-                                    1, !umem->writable, page_list, vma_list);
+                                    gup_flags, page_list, vma_list);
 
                if (ret < 0)
                        goto out;
index 75077a018675e1aa77c7955878250ad521c9d25d..1f0fe3217f2386e03caf4b7e14cd228bddac2020 100644 (file)
@@ -527,6 +527,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt,
        u64 off;
        int j, k, ret = 0, start_idx, npages = 0;
        u64 base_virt_addr;
+       unsigned int flags = 0;
 
        if (access_mask == 0)
                return -EINVAL;
@@ -556,6 +557,9 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt,
                goto out_put_task;
        }
 
+       if (access_mask & ODP_WRITE_ALLOWED_BIT)
+               flags |= FOLL_WRITE;
+
        start_idx = (user_virt - ib_umem_start(umem)) >> PAGE_SHIFT;
        k = start_idx;
 
@@ -574,8 +578,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt,
                 */
                npages = get_user_pages_remote(owning_process, owning_mm,
                                user_virt, gup_num_pages,
-                               access_mask & ODP_WRITE_ALLOWED_BIT,
-                               0, local_page_list, NULL);
+                               flags, local_page_list, NULL);
                up_read(&owning_mm->mmap_sem);
 
                if (npages < 0)
index 21fe401ff178b96ba1c9c7a52c31e03d09505c3e..e7a5ed9f6f3fbbe6f3bc146469e3a00d0729b04f 100644 (file)
@@ -10,3 +10,4 @@ obj-$(CONFIG_INFINIBAND_OCRDMA)               += ocrdma/
 obj-$(CONFIG_INFINIBAND_USNIC)         += usnic/
 obj-$(CONFIG_INFINIBAND_HFI1)          += hfi1/
 obj-$(CONFIG_INFINIBAND_HNS)           += hns/
+obj-$(CONFIG_INFINIBAND_QEDR)          += qedr/
index 875597b0e69c59196e666e958e7b25404779eb20..097365932b0961f578fe27c7c8b650d564317476 100644 (file)
@@ -83,8 +83,7 @@ static int hns_roce_sw2hw_cq(struct hns_roce_dev *dev,
 static int hns_roce_cq_alloc(struct hns_roce_dev *hr_dev, int nent,
                             struct hns_roce_mtt *hr_mtt,
                             struct hns_roce_uar *hr_uar,
-                            struct hns_roce_cq *hr_cq, int vector,
-                            int collapsed)
+                            struct hns_roce_cq *hr_cq, int vector)
 {
        struct hns_roce_cmd_mailbox *mailbox = NULL;
        struct hns_roce_cq_table *cq_table = NULL;
@@ -153,6 +152,9 @@ static int hns_roce_cq_alloc(struct hns_roce_dev *hr_dev, int nent,
        hr_cq->cons_index = 0;
        hr_cq->uar = hr_uar;
 
+       atomic_set(&hr_cq->refcount, 1);
+       init_completion(&hr_cq->free);
+
        return 0;
 
 err_radix:
@@ -192,6 +194,11 @@ static void hns_roce_free_cq(struct hns_roce_dev *hr_dev,
        /* Waiting interrupt process procedure carried out */
        synchronize_irq(hr_dev->eq_table.eq[hr_cq->vector].irq);
 
+       /* wait for all interrupt processed */
+       if (atomic_dec_and_test(&hr_cq->refcount))
+               complete(&hr_cq->free);
+       wait_for_completion(&hr_cq->free);
+
        spin_lock_irq(&cq_table->lock);
        radix_tree_delete(&cq_table->tree, hr_cq->cqn);
        spin_unlock_irq(&cq_table->lock);
@@ -300,10 +307,7 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
 
        cq_entries = roundup_pow_of_two((unsigned int)cq_entries);
        hr_cq->ib_cq.cqe = cq_entries - 1;
-       mutex_init(&hr_cq->resize_mutex);
        spin_lock_init(&hr_cq->lock);
-       hr_cq->hr_resize_buf = NULL;
-       hr_cq->resize_umem = NULL;
 
        if (context) {
                if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) {
@@ -338,8 +342,8 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
        }
 
        /* Allocate cq index, fill cq_context */
-       ret = hns_roce_cq_alloc(hr_dev, cq_entries, &hr_cq->hr_buf.hr_mtt,
-                               uar, hr_cq, vector, 0);
+       ret = hns_roce_cq_alloc(hr_dev, cq_entries, &hr_cq->hr_buf.hr_mtt, uar,
+                               hr_cq, vector);
        if (ret) {
                dev_err(dev, "Creat CQ .Failed to cq_alloc.\n");
                goto err_mtt;
@@ -353,12 +357,15 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
        if (context) {
                if (ib_copy_to_udata(udata, &hr_cq->cqn, sizeof(u64))) {
                        ret = -EFAULT;
-                       goto err_mtt;
+                       goto err_cqc;
                }
        }
 
        return &hr_cq->ib_cq;
 
+err_cqc:
+       hns_roce_free_cq(hr_dev, hr_cq);
+
 err_mtt:
        hns_roce_mtt_cleanup(hr_dev, &hr_cq->hr_buf.hr_mtt);
        if (context)
index ea735800eb18e8c6c5962349aa94152b13d636bf..341731553a60167d4fff793edb49dc8958371422 100644 (file)
@@ -62,7 +62,7 @@
 #define HNS_ROCE_AEQE_OF_VEC_NUM               1
 
 /* 4G/4K = 1M */
-#define HNS_ROCE_SL_SHIFT                      29
+#define HNS_ROCE_SL_SHIFT                      28
 #define HNS_ROCE_TCLASS_SHIFT                  20
 #define HNS_ROCE_FLOW_LABLE_MASK               0xfffff
 
@@ -74,7 +74,9 @@
 #define MR_TYPE_DMA                            0x03
 
 #define PKEY_ID                                        0xffff
+#define GUID_LEN                               8
 #define NODE_DESC_SIZE                         64
+#define DB_REG_OFFSET                          0x1000
 
 #define SERV_TYPE_RC                           0
 #define SERV_TYPE_RD                           1
@@ -282,20 +284,11 @@ struct hns_roce_cq_buf {
        struct hns_roce_mtt hr_mtt;
 };
 
-struct hns_roce_cq_resize {
-       struct hns_roce_cq_buf  hr_buf;
-       int                     cqe;
-};
-
 struct hns_roce_cq {
        struct ib_cq                    ib_cq;
        struct hns_roce_cq_buf          hr_buf;
-       /* pointer to store information after resize*/
-       struct hns_roce_cq_resize       *hr_resize_buf;
        spinlock_t                      lock;
-       struct mutex                    resize_mutex;
        struct ib_umem                  *umem;
-       struct ib_umem                  *resize_umem;
        void (*comp)(struct hns_roce_cq *);
        void (*event)(struct hns_roce_cq *, enum hns_roce_event);
 
@@ -408,6 +401,7 @@ struct hns_roce_qp {
        u32                     buff_size;
        struct mutex            mutex;
        u8                      port;
+       u8                      phy_port;
        u8                      sl;
        u8                      resp_depth;
        u8                      state;
@@ -471,7 +465,6 @@ struct hns_roce_caps {
        u32             max_rq_desc_sz; /* 64 */
        int             max_qp_init_rdma;
        int             max_qp_dest_rdma;
-       int             sqp_start;
        int             num_cqs;
        int             max_cqes;
        int             reserved_cqs;
@@ -512,6 +505,8 @@ struct hns_roce_hw {
        void (*write_cqc)(struct hns_roce_dev *hr_dev,
                          struct hns_roce_cq *hr_cq, void *mb_buf, u64 *mtts,
                          dma_addr_t dma_handle, int nent, u32 vector);
+       int (*clear_hem)(struct hns_roce_dev *hr_dev,
+                        struct hns_roce_hem_table *table, int obj);
        int (*query_qp)(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr,
                        int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr);
        int (*modify_qp)(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
@@ -533,7 +528,6 @@ struct hns_roce_dev {
        struct hns_roce_uar     priv_uar;
        const char              *irq_names[HNS_ROCE_MAX_IRQ_NUM];
        spinlock_t              sm_lock;
-       spinlock_t              cq_db_lock;
        spinlock_t              bt_cmd_lock;
        struct hns_roce_ib_iboe iboe;
 
index 98af7fecf2f1e9d42d92fec1a776c1e1bc6d3080..21e21b03cfb52a966f1412e65b2ec4709975ecae 100644 (file)
@@ -66,9 +66,6 @@ static void hns_roce_wq_catas_err_handle(struct hns_roce_dev *hr_dev,
 {
        struct device *dev = &hr_dev->pdev->dev;
 
-       qpn = roce_get_field(aeqe->event.qp_event.qp,
-                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S);
        dev_warn(dev, "Local Work Queue Catastrophic Error.\n");
        switch (roce_get_field(aeqe->asyn, HNS_ROCE_AEQE_U32_4_EVENT_SUB_TYPE_M,
                               HNS_ROCE_AEQE_U32_4_EVENT_SUB_TYPE_S)) {
@@ -96,13 +93,6 @@ static void hns_roce_wq_catas_err_handle(struct hns_roce_dev *hr_dev,
        default:
                break;
        }
-
-       hns_roce_qp_event(hr_dev, roce_get_field(aeqe->event.qp_event.qp,
-                                       HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                                       HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S),
-                         roce_get_field(aeqe->asyn,
-                                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
 }
 
 static void hns_roce_local_wq_access_err_handle(struct hns_roce_dev *hr_dev,
@@ -111,9 +101,6 @@ static void hns_roce_local_wq_access_err_handle(struct hns_roce_dev *hr_dev,
 {
        struct device *dev = &hr_dev->pdev->dev;
 
-       qpn = roce_get_field(aeqe->event.qp_event.qp,
-                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S);
        dev_warn(dev, "Local Access Violation Work Queue Error.\n");
        switch (roce_get_field(aeqe->asyn, HNS_ROCE_AEQE_U32_4_EVENT_SUB_TYPE_M,
                               HNS_ROCE_AEQE_U32_4_EVENT_SUB_TYPE_S)) {
@@ -141,13 +128,69 @@ static void hns_roce_local_wq_access_err_handle(struct hns_roce_dev *hr_dev,
        default:
                break;
        }
+}
+
+static void hns_roce_qp_err_handle(struct hns_roce_dev *hr_dev,
+                                  struct hns_roce_aeqe *aeqe,
+                                  int event_type)
+{
+       struct device *dev = &hr_dev->pdev->dev;
+       int phy_port;
+       int qpn;
+
+       qpn = roce_get_field(aeqe->event.qp_event.qp,
+                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
+                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S);
+       phy_port = roce_get_field(aeqe->event.qp_event.qp,
+                       HNS_ROCE_AEQE_EVENT_QP_EVENT_PORT_NUM_M,
+                       HNS_ROCE_AEQE_EVENT_QP_EVENT_PORT_NUM_S);
+       if (qpn <= 1)
+               qpn = HNS_ROCE_MAX_PORTS * qpn + phy_port;
+
+       switch (event_type) {
+       case HNS_ROCE_EVENT_TYPE_INV_REQ_LOCAL_WQ_ERROR:
+               dev_warn(dev, "Invalid Req Local Work Queue Error.\n"
+                             "QP %d, phy_port %d.\n", qpn, phy_port);
+               break;
+       case HNS_ROCE_EVENT_TYPE_WQ_CATAS_ERROR:
+               hns_roce_wq_catas_err_handle(hr_dev, aeqe, qpn);
+               break;
+       case HNS_ROCE_EVENT_TYPE_LOCAL_WQ_ACCESS_ERROR:
+               hns_roce_local_wq_access_err_handle(hr_dev, aeqe, qpn);
+               break;
+       default:
+               break;
+       }
+
+       hns_roce_qp_event(hr_dev, qpn, event_type);
+}
+
+static void hns_roce_cq_err_handle(struct hns_roce_dev *hr_dev,
+                                  struct hns_roce_aeqe *aeqe,
+                                  int event_type)
+{
+       struct device *dev = &hr_dev->pdev->dev;
+       u32 cqn;
+
+       cqn = le32_to_cpu(roce_get_field(aeqe->event.cq_event.cq,
+                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
+                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S));
+
+       switch (event_type) {
+       case HNS_ROCE_EVENT_TYPE_CQ_ACCESS_ERROR:
+               dev_warn(dev, "CQ 0x%x access err.\n", cqn);
+               break;
+       case HNS_ROCE_EVENT_TYPE_CQ_OVERFLOW:
+               dev_warn(dev, "CQ 0x%x overflow\n", cqn);
+               break;
+       case HNS_ROCE_EVENT_TYPE_CQ_ID_INVALID:
+               dev_warn(dev, "CQ 0x%x ID invalid.\n", cqn);
+               break;
+       default:
+               break;
+       }
 
-       hns_roce_qp_event(hr_dev, roce_get_field(aeqe->event.qp_event.qp,
-                                        HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                                        HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S),
-                         roce_get_field(aeqe->asyn,
-                                        HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                        HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
+       hns_roce_cq_event(hr_dev, cqn, event_type);
 }
 
 static void hns_roce_db_overflow_handle(struct hns_roce_dev *hr_dev,
@@ -185,7 +228,7 @@ static int hns_roce_aeq_int(struct hns_roce_dev *hr_dev, struct hns_roce_eq *eq)
        struct device *dev = &hr_dev->pdev->dev;
        struct hns_roce_aeqe *aeqe;
        int aeqes_found = 0;
-       int qpn = 0;
+       int event_type;
 
        while ((aeqe = next_aeqe_sw(eq))) {
                dev_dbg(dev, "aeqe = %p, aeqe->asyn.event_type = 0x%lx\n", aeqe,
@@ -195,9 +238,10 @@ static int hns_roce_aeq_int(struct hns_roce_dev *hr_dev, struct hns_roce_eq *eq)
                /* Memory barrier */
                rmb();
 
-               switch (roce_get_field(aeqe->asyn,
-                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S)) {
+               event_type = roce_get_field(aeqe->asyn,
+                               HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
+                               HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S);
+               switch (event_type) {
                case HNS_ROCE_EVENT_TYPE_PATH_MIG:
                        dev_warn(dev, "PATH MIG not supported\n");
                        break;
@@ -211,23 +255,9 @@ static int hns_roce_aeq_int(struct hns_roce_dev *hr_dev, struct hns_roce_eq *eq)
                        dev_warn(dev, "PATH MIG failed\n");
                        break;
                case HNS_ROCE_EVENT_TYPE_INV_REQ_LOCAL_WQ_ERROR:
-                       dev_warn(dev, "qpn = 0x%lx\n",
-                       roce_get_field(aeqe->event.qp_event.qp,
-                                      HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                                      HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S));
-                       hns_roce_qp_event(hr_dev,
-                               roce_get_field(aeqe->event.qp_event.qp,
-                                       HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                                       HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S),
-                               roce_get_field(aeqe->asyn,
-                                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
-                       break;
                case HNS_ROCE_EVENT_TYPE_WQ_CATAS_ERROR:
-                       hns_roce_wq_catas_err_handle(hr_dev, aeqe, qpn);
-                       break;
                case HNS_ROCE_EVENT_TYPE_LOCAL_WQ_ACCESS_ERROR:
-                       hns_roce_local_wq_access_err_handle(hr_dev, aeqe, qpn);
+                       hns_roce_qp_err_handle(hr_dev, aeqe, event_type);
                        break;
                case HNS_ROCE_EVENT_TYPE_SRQ_LIMIT_REACH:
                case HNS_ROCE_EVENT_TYPE_SRQ_CATAS_ERROR:
@@ -235,40 +265,9 @@ static int hns_roce_aeq_int(struct hns_roce_dev *hr_dev, struct hns_roce_eq *eq)
                        dev_warn(dev, "SRQ not support!\n");
                        break;
                case HNS_ROCE_EVENT_TYPE_CQ_ACCESS_ERROR:
-                       dev_warn(dev, "CQ 0x%lx access err.\n",
-                       roce_get_field(aeqe->event.cq_event.cq,
-                                      HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
-                                      HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S));
-                       hns_roce_cq_event(hr_dev,
-                       le32_to_cpu(roce_get_field(aeqe->event.cq_event.cq,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S)),
-                       roce_get_field(aeqe->asyn,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
-                       break;
                case HNS_ROCE_EVENT_TYPE_CQ_OVERFLOW:
-                       dev_warn(dev, "CQ 0x%lx overflow\n",
-                       roce_get_field(aeqe->event.cq_event.cq,
-                                      HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
-                                      HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S));
-                       hns_roce_cq_event(hr_dev,
-                       le32_to_cpu(roce_get_field(aeqe->event.cq_event.cq,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S)),
-                       roce_get_field(aeqe->asyn,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
-                       break;
                case HNS_ROCE_EVENT_TYPE_CQ_ID_INVALID:
-                       dev_warn(dev, "CQ ID invalid.\n");
-                       hns_roce_cq_event(hr_dev,
-                       le32_to_cpu(roce_get_field(aeqe->event.cq_event.cq,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S)),
-                       roce_get_field(aeqe->asyn,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
+                       hns_roce_cq_err_handle(hr_dev, aeqe, event_type);
                        break;
                case HNS_ROCE_EVENT_TYPE_PORT_CHANGE:
                        dev_warn(dev, "port change.\n");
@@ -290,11 +289,8 @@ static int hns_roce_aeq_int(struct hns_roce_dev *hr_dev, struct hns_roce_eq *eq)
                                     HNS_ROCE_AEQE_EVENT_CE_EVENT_CEQE_CEQN_S));
                        break;
                default:
-                       dev_warn(dev, "Unhandled event 0x%lx on EQ %d at index %u\n",
-                                roce_get_field(aeqe->asyn,
-                                             HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                             HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S),
-                                eq->eqn, eq->cons_index);
+                       dev_warn(dev, "Unhandled event %d on EQ %d at index %u\n",
+                                event_type, eq->eqn, eq->cons_index);
                        break;
                };
 
index fe4388191a3c255c74a3668256a3a5e91f897772..c6d212d12e033372f1c1e99b46c764b8eefcde28 100644 (file)
@@ -107,6 +107,10 @@ struct hns_roce_aeqe {
 #define HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M   \
        (((1UL << 24) - 1) << HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S)
 
+#define HNS_ROCE_AEQE_EVENT_QP_EVENT_PORT_NUM_S 25
+#define HNS_ROCE_AEQE_EVENT_QP_EVENT_PORT_NUM_M   \
+       (((1UL << 3) - 1) << HNS_ROCE_AEQE_EVENT_QP_EVENT_PORT_NUM_S)
+
 #define HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S 0
 #define HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M   \
        (((1UL << 16) - 1) << HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S)
index d53d643623899e0fdd2eaf771dcb852bb817a6fe..250d8f2803908f4c5fcae2d224baae5df68ed1fb 100644 (file)
 #include "hns_roce_hem.h"
 #include "hns_roce_common.h"
 
-#define HW_SYNC_TIMEOUT_MSECS          500
-#define HW_SYNC_SLEEP_TIME_INTERVAL    20
-
 #define HNS_ROCE_HEM_ALLOC_SIZE                (1 << 17)
 #define HNS_ROCE_TABLE_CHUNK_SIZE      (1 << 17)
 
 #define DMA_ADDR_T_SHIFT               12
-#define BT_CMD_SYNC_SHIFT              31
 #define BT_BA_SHIFT                    32
 
 struct hns_roce_hem *hns_roce_alloc_hem(struct hns_roce_dev *hr_dev, int npages,
@@ -213,74 +209,6 @@ static int hns_roce_set_hem(struct hns_roce_dev *hr_dev,
        return ret;
 }
 
-static int hns_roce_clear_hem(struct hns_roce_dev *hr_dev,
-                             struct hns_roce_hem_table *table,
-                             unsigned long obj)
-{
-       struct device *dev = &hr_dev->pdev->dev;
-       unsigned long end = 0;
-       unsigned long flags;
-       void __iomem *bt_cmd;
-       uint32_t bt_cmd_val[2];
-       u32 bt_cmd_h_val = 0;
-       int ret = 0;
-
-       switch (table->type) {
-       case HEM_TYPE_QPC:
-               roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
-                              ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_QPC);
-               break;
-       case HEM_TYPE_MTPT:
-               roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
-                              ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S,
-                              HEM_TYPE_MTPT);
-               break;
-       case HEM_TYPE_CQC:
-               roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
-                              ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_CQC);
-               break;
-       case HEM_TYPE_SRQC:
-               roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
-                              ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S,
-                              HEM_TYPE_SRQC);
-               break;
-       default:
-               return ret;
-       }
-       roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_IN_MDF_M,
-                      ROCEE_BT_CMD_H_ROCEE_BT_CMD_IN_MDF_S, obj);
-       roce_set_bit(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_S, 0);
-       roce_set_bit(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_HW_SYNS_S, 1);
-       roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_BA_H_M,
-                      ROCEE_BT_CMD_H_ROCEE_BT_CMD_BA_H_S, 0);
-
-       spin_lock_irqsave(&hr_dev->bt_cmd_lock, flags);
-
-       bt_cmd = hr_dev->reg_base + ROCEE_BT_CMD_H_REG;
-
-       end = msecs_to_jiffies(HW_SYNC_TIMEOUT_MSECS) + jiffies;
-       while (1) {
-               if (readl(bt_cmd) >> BT_CMD_SYNC_SHIFT) {
-                       if (!(time_before(jiffies, end))) {
-                               dev_err(dev, "Write bt_cmd err,hw_sync is not zero.\n");
-                               spin_unlock_irqrestore(&hr_dev->bt_cmd_lock,
-                                                      flags);
-                               return -EBUSY;
-                       }
-               } else {
-                       break;
-               }
-               msleep(HW_SYNC_SLEEP_TIME_INTERVAL);
-       }
-
-       bt_cmd_val[0] = 0;
-       bt_cmd_val[1] = bt_cmd_h_val;
-       hns_roce_write64_k(bt_cmd_val, hr_dev->reg_base + ROCEE_BT_CMD_L_REG);
-       spin_unlock_irqrestore(&hr_dev->bt_cmd_lock, flags);
-
-       return ret;
-}
-
 int hns_roce_table_get(struct hns_roce_dev *hr_dev,
                       struct hns_roce_hem_table *table, unsigned long obj)
 {
@@ -333,7 +261,7 @@ void hns_roce_table_put(struct hns_roce_dev *hr_dev,
 
        if (--table->hem[i]->refcount == 0) {
                /* Clear HEM base address */
-               if (hns_roce_clear_hem(hr_dev, table, obj))
+               if (hr_dev->hw->clear_hem(hr_dev, table, obj))
                        dev_warn(dev, "Clear HEM base address failed.\n");
 
                hns_roce_free_hem(hr_dev, table->hem[i]);
@@ -456,7 +384,7 @@ void hns_roce_cleanup_hem_table(struct hns_roce_dev *hr_dev,
 
        for (i = 0; i < table->num_hem; ++i)
                if (table->hem[i]) {
-                       if (hns_roce_clear_hem(hr_dev, table,
+                       if (hr_dev->hw->clear_hem(hr_dev, table,
                            i * HNS_ROCE_TABLE_CHUNK_SIZE / table->obj_size))
                                dev_err(dev, "Clear HEM base address failed.\n");
 
index ad6617588fba9a15eee65e12eb948219ba706670..435748858252d756d065315e5ce4347db46c4477 100644 (file)
 #ifndef _HNS_ROCE_HEM_H
 #define _HNS_ROCE_HEM_H
 
+#define HW_SYNC_TIMEOUT_MSECS          500
+#define HW_SYNC_SLEEP_TIME_INTERVAL    20
+#define BT_CMD_SYNC_SHIFT              31
+
 enum {
        /* MAP HEM(Hardware Entry Memory) */
        HEM_TYPE_QPC = 0,
index 399f5dedaf2dd6dfcd075d7875903b943bed4ec3..71232e5fabf6acccf4002f1fedded380f4376274 100644 (file)
@@ -73,8 +73,14 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
        u32 ind = 0;
        int ret = 0;
 
-       spin_lock_irqsave(&qp->sq.lock, flags);
+       if (unlikely(ibqp->qp_type != IB_QPT_GSI &&
+               ibqp->qp_type != IB_QPT_RC)) {
+               dev_err(dev, "un-supported QP type\n");
+               *bad_wr = NULL;
+               return -EOPNOTSUPP;
+       }
 
+       spin_lock_irqsave(&qp->sq.lock, flags);
        ind = qp->sq_next_wqe;
        for (nreq = 0; wr; ++nreq, wr = wr->next) {
                if (hns_roce_wq_overflow(&qp->sq, nreq, qp->ibqp.send_cq)) {
@@ -162,7 +168,7 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                        roce_set_field(ud_sq_wqe->u32_36,
                                       UD_SEND_WQE_U32_36_SGID_INDEX_M,
                                       UD_SEND_WQE_U32_36_SGID_INDEX_S,
-                                      hns_get_gid_index(hr_dev, qp->port,
+                                      hns_get_gid_index(hr_dev, qp->phy_port,
                                                         ah->av.gid_index));
 
                        roce_set_field(ud_sq_wqe->u32_40,
@@ -205,8 +211,7 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                                      (wr->send_flags & IB_SEND_FENCE ?
                                      (cpu_to_le32(HNS_ROCE_WQE_FENCE)) : 0);
 
-                       wqe = (struct hns_roce_wqe_ctrl_seg *)wqe +
-                              sizeof(struct hns_roce_wqe_ctrl_seg);
+                       wqe += sizeof(struct hns_roce_wqe_ctrl_seg);
 
                        switch (wr->opcode) {
                        case IB_WR_RDMA_READ:
@@ -235,8 +240,7 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                                break;
                        }
                        ctrl->flag |= cpu_to_le32(ps_opcode);
-                       wqe = (struct hns_roce_wqe_raddr_seg *)wqe +
-                              sizeof(struct hns_roce_wqe_raddr_seg);
+                       wqe += sizeof(struct hns_roce_wqe_raddr_seg);
 
                        dseg = wqe;
                        if (wr->send_flags & IB_SEND_INLINE && wr->num_sge) {
@@ -253,8 +257,7 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                                        memcpy(wqe, ((void *) (uintptr_t)
                                               wr->sg_list[i].addr),
                                               wr->sg_list[i].length);
-                                       wqe = (struct hns_roce_wqe_raddr_seg *)
-                                              wqe + wr->sg_list[i].length;
+                                       wqe += wr->sg_list[i].length;
                                }
                                ctrl->flag |= HNS_ROCE_WQE_INLINE;
                        } else {
@@ -266,9 +269,6 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                                              HNS_ROCE_WQE_SGE_NUM_BIT);
                        }
                        ind++;
-               } else {
-                       dev_dbg(dev, "unSupported QP type\n");
-                       break;
                }
        }
 
@@ -285,7 +285,7 @@ out:
                               SQ_DOORBELL_U32_4_SQ_HEAD_S,
                              (qp->sq.head & ((qp->sq.wqe_cnt << 1) - 1)));
                roce_set_field(sq_db.u32_4, SQ_DOORBELL_U32_4_PORT_M,
-                              SQ_DOORBELL_U32_4_PORT_S, qp->port);
+                              SQ_DOORBELL_U32_4_PORT_S, qp->phy_port);
                roce_set_field(sq_db.u32_8, SQ_DOORBELL_U32_8_QPN_M,
                               SQ_DOORBELL_U32_8_QPN_S, qp->doorbell_qpn);
                roce_set_bit(sq_db.u32_8, SQ_DOORBELL_HW_SYNC_S, 1);
@@ -365,14 +365,14 @@ out:
                        /* SW update GSI rq header */
                        reg_val = roce_read(to_hr_dev(ibqp->device),
                                            ROCEE_QP1C_CFG3_0_REG +
-                                           QP1C_CFGN_OFFSET * hr_qp->port);
+                                           QP1C_CFGN_OFFSET * hr_qp->phy_port);
                        roce_set_field(reg_val,
                                       ROCEE_QP1C_CFG3_0_ROCEE_QP1C_RQ_HEAD_M,
                                       ROCEE_QP1C_CFG3_0_ROCEE_QP1C_RQ_HEAD_S,
                                       hr_qp->rq.head);
                        roce_write(to_hr_dev(ibqp->device),
                                   ROCEE_QP1C_CFG3_0_REG +
-                                  QP1C_CFGN_OFFSET * hr_qp->port, reg_val);
+                                  QP1C_CFGN_OFFSET * hr_qp->phy_port, reg_val);
                } else {
                        rq_db.u32_4 = 0;
                        rq_db.u32_8 = 0;
@@ -789,6 +789,66 @@ static void hns_roce_port_enable(struct hns_roce_dev *hr_dev, int enable_flag)
        }
 }
 
+static int hns_roce_bt_init(struct hns_roce_dev *hr_dev)
+{
+       struct device *dev = &hr_dev->pdev->dev;
+       struct hns_roce_v1_priv *priv;
+       int ret;
+
+       priv = (struct hns_roce_v1_priv *)hr_dev->hw->priv;
+
+       priv->bt_table.qpc_buf.buf = dma_alloc_coherent(dev,
+               HNS_ROCE_BT_RSV_BUF_SIZE, &priv->bt_table.qpc_buf.map,
+               GFP_KERNEL);
+       if (!priv->bt_table.qpc_buf.buf)
+               return -ENOMEM;
+
+       priv->bt_table.mtpt_buf.buf = dma_alloc_coherent(dev,
+               HNS_ROCE_BT_RSV_BUF_SIZE, &priv->bt_table.mtpt_buf.map,
+               GFP_KERNEL);
+       if (!priv->bt_table.mtpt_buf.buf) {
+               ret = -ENOMEM;
+               goto err_failed_alloc_mtpt_buf;
+       }
+
+       priv->bt_table.cqc_buf.buf = dma_alloc_coherent(dev,
+               HNS_ROCE_BT_RSV_BUF_SIZE, &priv->bt_table.cqc_buf.map,
+               GFP_KERNEL);
+       if (!priv->bt_table.cqc_buf.buf) {
+               ret = -ENOMEM;
+               goto err_failed_alloc_cqc_buf;
+       }
+
+       return 0;
+
+err_failed_alloc_cqc_buf:
+       dma_free_coherent(dev, HNS_ROCE_BT_RSV_BUF_SIZE,
+               priv->bt_table.mtpt_buf.buf, priv->bt_table.mtpt_buf.map);
+
+err_failed_alloc_mtpt_buf:
+       dma_free_coherent(dev, HNS_ROCE_BT_RSV_BUF_SIZE,
+               priv->bt_table.qpc_buf.buf, priv->bt_table.qpc_buf.map);
+
+       return ret;
+}
+
+static void hns_roce_bt_free(struct hns_roce_dev *hr_dev)
+{
+       struct device *dev = &hr_dev->pdev->dev;
+       struct hns_roce_v1_priv *priv;
+
+       priv = (struct hns_roce_v1_priv *)hr_dev->hw->priv;
+
+       dma_free_coherent(dev, HNS_ROCE_BT_RSV_BUF_SIZE,
+               priv->bt_table.cqc_buf.buf, priv->bt_table.cqc_buf.map);
+
+       dma_free_coherent(dev, HNS_ROCE_BT_RSV_BUF_SIZE,
+               priv->bt_table.mtpt_buf.buf, priv->bt_table.mtpt_buf.map);
+
+       dma_free_coherent(dev, HNS_ROCE_BT_RSV_BUF_SIZE,
+               priv->bt_table.qpc_buf.buf, priv->bt_table.qpc_buf.map);
+}
+
 /**
  * hns_roce_v1_reset - reset RoCE
  * @hr_dev: RoCE device struct pointer
@@ -879,7 +939,6 @@ void hns_roce_v1_profile(struct hns_roce_dev *hr_dev)
        caps->mtt_entry_sz      = HNS_ROCE_V1_MTT_ENTRY_SIZE;
        caps->cq_entry_sz       = HNS_ROCE_V1_CQE_ENTRY_SIZE;
        caps->page_size_cap     = HNS_ROCE_V1_PAGE_SIZE_SUPPORT;
-       caps->sqp_start         = 0;
        caps->reserved_lkey     = 0;
        caps->reserved_pds      = 0;
        caps->reserved_mrws     = 1;
@@ -944,8 +1003,18 @@ int hns_roce_v1_init(struct hns_roce_dev *hr_dev)
 
        hns_roce_port_enable(hr_dev, HNS_ROCE_PORT_UP);
 
+       ret = hns_roce_bt_init(hr_dev);
+       if (ret) {
+               dev_err(dev, "bt init failed!\n");
+               goto error_failed_bt_init;
+       }
+
        return 0;
 
+error_failed_bt_init:
+       hns_roce_port_enable(hr_dev, HNS_ROCE_PORT_DOWN);
+       hns_roce_raq_free(hr_dev);
+
 error_failed_raq_init:
        hns_roce_db_free(hr_dev);
        return ret;
@@ -953,6 +1022,7 @@ error_failed_raq_init:
 
 void hns_roce_v1_exit(struct hns_roce_dev *hr_dev)
 {
+       hns_roce_bt_free(hr_dev);
        hns_roce_port_enable(hr_dev, HNS_ROCE_PORT_DOWN);
        hns_roce_raq_free(hr_dev);
        hns_roce_db_free(hr_dev);
@@ -1192,9 +1262,7 @@ static struct hns_roce_cqe *next_cqe_sw(struct hns_roce_cq *hr_cq)
        return get_sw_cqe(hr_cq, hr_cq->cons_index);
 }
 
-void hns_roce_v1_cq_set_ci(struct hns_roce_cq *hr_cq, u32 cons_index,
-                          spinlock_t *doorbell_lock)
-
+void hns_roce_v1_cq_set_ci(struct hns_roce_cq *hr_cq, u32 cons_index)
 {
        u32 doorbell[2];
 
@@ -1254,8 +1322,7 @@ static void __hns_roce_v1_cq_clean(struct hns_roce_cq *hr_cq, u32 qpn,
                */
                wmb();
 
-               hns_roce_v1_cq_set_ci(hr_cq, hr_cq->cons_index,
-                                  &to_hr_dev(hr_cq->ib_cq.device)->cq_db_lock);
+               hns_roce_v1_cq_set_ci(hr_cq, hr_cq->cons_index);
        }
 }
 
@@ -1485,7 +1552,8 @@ static int hns_roce_v1_poll_one(struct hns_roce_cq *hr_cq,
                /* SQ conrespond to CQE */
                sq_wqe = get_send_wqe(*cur_qp, roce_get_field(cqe->cqe_byte_4,
                                                CQE_BYTE_4_WQE_INDEX_M,
-                                               CQE_BYTE_4_WQE_INDEX_S));
+                                               CQE_BYTE_4_WQE_INDEX_S)&
+                                               ((*cur_qp)->sq.wqe_cnt-1));
                switch (sq_wqe->flag & HNS_ROCE_WQE_OPCODE_MASK) {
                case HNS_ROCE_WQE_OPCODE_SEND:
                        wc->opcode = IB_WC_SEND;
@@ -1591,10 +1659,8 @@ int hns_roce_v1_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
                        break;
        }
 
-       if (npolled) {
-               hns_roce_v1_cq_set_ci(hr_cq, hr_cq->cons_index,
-                                     &to_hr_dev(ibcq->device)->cq_db_lock);
-       }
+       if (npolled)
+               hns_roce_v1_cq_set_ci(hr_cq, hr_cq->cons_index);
 
        spin_unlock_irqrestore(&hr_cq->lock, flags);
 
@@ -1604,6 +1670,74 @@ int hns_roce_v1_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
                return ret;
 }
 
+int hns_roce_v1_clear_hem(struct hns_roce_dev *hr_dev,
+               struct hns_roce_hem_table *table, int obj)
+{
+       struct device *dev = &hr_dev->pdev->dev;
+       struct hns_roce_v1_priv *priv;
+       unsigned long end = 0, flags = 0;
+       uint32_t bt_cmd_val[2] = {0};
+       void __iomem *bt_cmd;
+       u64 bt_ba = 0;
+
+       priv = (struct hns_roce_v1_priv *)hr_dev->hw->priv;
+
+       switch (table->type) {
+       case HEM_TYPE_QPC:
+               roce_set_field(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
+                       ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_QPC);
+               bt_ba = priv->bt_table.qpc_buf.map >> 12;
+               break;
+       case HEM_TYPE_MTPT:
+               roce_set_field(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
+                       ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_MTPT);
+               bt_ba = priv->bt_table.mtpt_buf.map >> 12;
+               break;
+       case HEM_TYPE_CQC:
+               roce_set_field(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
+                       ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_CQC);
+               bt_ba = priv->bt_table.cqc_buf.map >> 12;
+               break;
+       case HEM_TYPE_SRQC:
+               dev_dbg(dev, "HEM_TYPE_SRQC not support.\n");
+               return -EINVAL;
+       default:
+               return 0;
+       }
+       roce_set_field(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_IN_MDF_M,
+               ROCEE_BT_CMD_H_ROCEE_BT_CMD_IN_MDF_S, obj);
+       roce_set_bit(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_S, 0);
+       roce_set_bit(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_HW_SYNS_S, 1);
+
+       spin_lock_irqsave(&hr_dev->bt_cmd_lock, flags);
+
+       bt_cmd = hr_dev->reg_base + ROCEE_BT_CMD_H_REG;
+
+       end = msecs_to_jiffies(HW_SYNC_TIMEOUT_MSECS) + jiffies;
+       while (1) {
+               if (readl(bt_cmd) >> BT_CMD_SYNC_SHIFT) {
+                       if (!(time_before(jiffies, end))) {
+                               dev_err(dev, "Write bt_cmd err,hw_sync is not zero.\n");
+                               spin_unlock_irqrestore(&hr_dev->bt_cmd_lock,
+                                       flags);
+                               return -EBUSY;
+                       }
+               } else {
+                       break;
+               }
+               msleep(HW_SYNC_SLEEP_TIME_INTERVAL);
+       }
+
+       bt_cmd_val[0] = (uint32_t)bt_ba;
+       roce_set_field(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_BA_H_M,
+               ROCEE_BT_CMD_H_ROCEE_BT_CMD_BA_H_S, bt_ba >> 32);
+       hns_roce_write64_k(bt_cmd_val, hr_dev->reg_base + ROCEE_BT_CMD_L_REG);
+
+       spin_unlock_irqrestore(&hr_dev->bt_cmd_lock, flags);
+
+       return 0;
+}
+
 static int hns_roce_v1_qp_modify(struct hns_roce_dev *hr_dev,
                                 struct hns_roce_mtt *mtt,
                                 enum hns_roce_qp_state cur_state,
@@ -1733,13 +1867,10 @@ static int hns_roce_v1_m_sqp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                roce_set_field(context->qp1c_bytes_16, QP1C_BYTES_16_RQ_HEAD_M,
                               QP1C_BYTES_16_RQ_HEAD_S, hr_qp->rq.head);
                roce_set_field(context->qp1c_bytes_16, QP1C_BYTES_16_PORT_NUM_M,
-                              QP1C_BYTES_16_PORT_NUM_S, hr_qp->port);
+                              QP1C_BYTES_16_PORT_NUM_S, hr_qp->phy_port);
                roce_set_bit(context->qp1c_bytes_16,
                             QP1C_BYTES_16_SIGNALING_TYPE_S,
                             hr_qp->sq_signal_bits);
-               roce_set_bit(context->qp1c_bytes_16,
-                            QP1C_BYTES_16_LOCAL_ENABLE_E2E_CREDIT_S,
-                            hr_qp->sq_signal_bits);
                roce_set_bit(context->qp1c_bytes_16, QP1C_BYTES_16_RQ_BA_FLG_S,
                             1);
                roce_set_bit(context->qp1c_bytes_16, QP1C_BYTES_16_SQ_BA_FLG_S,
@@ -1784,7 +1915,7 @@ static int hns_roce_v1_m_sqp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
 
                /* Copy context to QP1C register */
                addr = (u32 *)(hr_dev->reg_base + ROCEE_QP1C_CFG0_0_REG +
-                       hr_qp->port * sizeof(*context));
+                       hr_qp->phy_port * sizeof(*context));
 
                writel(context->qp1c_bytes_4, addr);
                writel(context->sq_rq_bt_l, addr + 1);
@@ -1795,15 +1926,16 @@ static int hns_roce_v1_m_sqp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                writel(context->qp1c_bytes_28, addr + 6);
                writel(context->qp1c_bytes_32, addr + 7);
                writel(context->cur_sq_wqe_ba_l, addr + 8);
+               writel(context->qp1c_bytes_40, addr + 9);
        }
 
        /* Modify QP1C status */
        reg_val = roce_read(hr_dev, ROCEE_QP1C_CFG0_0_REG +
-                           hr_qp->port * sizeof(*context));
+                           hr_qp->phy_port * sizeof(*context));
        roce_set_field(reg_val, ROCEE_QP1C_CFG0_0_ROCEE_QP1C_QP_ST_M,
                       ROCEE_QP1C_CFG0_0_ROCEE_QP1C_QP_ST_S, new_state);
        roce_write(hr_dev, ROCEE_QP1C_CFG0_0_REG +
-                   hr_qp->port * sizeof(*context), reg_val);
+                   hr_qp->phy_port * sizeof(*context), reg_val);
 
        hr_qp->state = new_state;
        if (new_state == IB_QPS_RESET) {
@@ -1836,12 +1968,10 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
        struct hns_roce_qp *hr_qp = to_hr_qp(ibqp);
        struct device *dev = &hr_dev->pdev->dev;
        struct hns_roce_qp_context *context;
-       struct hns_roce_rq_db rq_db;
        dma_addr_t dma_handle_2 = 0;
        dma_addr_t dma_handle = 0;
        uint32_t doorbell[2] = {0};
        int rq_pa_start = 0;
-       u32 reg_val = 0;
        u64 *mtts_2 = NULL;
        int ret = -EINVAL;
        u64 *mtts = NULL;
@@ -2119,7 +2249,8 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
 
                roce_set_field(context->qpc_bytes_68,
                               QP_CONTEXT_QPC_BYTES_68_RQ_HEAD_M,
-                              QP_CONTEXT_QPC_BYTES_68_RQ_HEAD_S, 0);
+                              QP_CONTEXT_QPC_BYTES_68_RQ_HEAD_S,
+                              hr_qp->rq.head);
                roce_set_field(context->qpc_bytes_68,
                               QP_CONTEXT_QPC_BYTES_68_RQ_CUR_INDEX_M,
                               QP_CONTEXT_QPC_BYTES_68_RQ_CUR_INDEX_S, 0);
@@ -2186,7 +2317,7 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                roce_set_field(context->qpc_bytes_156,
                               QP_CONTEXT_QPC_BYTES_156_PORT_NUM_M,
                               QP_CONTEXT_QPC_BYTES_156_PORT_NUM_S,
-                              hr_qp->port);
+                              hr_qp->phy_port);
                roce_set_field(context->qpc_bytes_156,
                               QP_CONTEXT_QPC_BYTES_156_SL_M,
                               QP_CONTEXT_QPC_BYTES_156_SL_S, attr->ah_attr.sl);
@@ -2257,20 +2388,17 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                roce_set_bit(context->qpc_bytes_140,
                             QP_CONTEXT_QPC_BYTES_140_RNR_RETRY_FLG_S, 0);
 
-               roce_set_field(context->qpc_bytes_144,
-                              QP_CONTEXT_QPC_BYTES_144_QP_STATE_M,
-                              QP_CONTEXT_QPC_BYTES_144_QP_STATE_S,
-                              attr->qp_state);
-
                roce_set_field(context->qpc_bytes_148,
                               QP_CONTEXT_QPC_BYTES_148_CHECK_FLAG_M,
                               QP_CONTEXT_QPC_BYTES_148_CHECK_FLAG_S, 0);
                roce_set_field(context->qpc_bytes_148,
                               QP_CONTEXT_QPC_BYTES_148_RETRY_COUNT_M,
-                              QP_CONTEXT_QPC_BYTES_148_RETRY_COUNT_S, 0);
+                              QP_CONTEXT_QPC_BYTES_148_RETRY_COUNT_S,
+                              attr->retry_cnt);
                roce_set_field(context->qpc_bytes_148,
                               QP_CONTEXT_QPC_BYTES_148_RNR_RETRY_COUNT_M,
-                              QP_CONTEXT_QPC_BYTES_148_RNR_RETRY_COUNT_S, 0);
+                              QP_CONTEXT_QPC_BYTES_148_RNR_RETRY_COUNT_S,
+                              attr->rnr_retry);
                roce_set_field(context->qpc_bytes_148,
                               QP_CONTEXT_QPC_BYTES_148_LSN_M,
                               QP_CONTEXT_QPC_BYTES_148_LSN_S, 0x100);
@@ -2281,10 +2409,19 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                               QP_CONTEXT_QPC_BYTES_156_RETRY_COUNT_INIT_M,
                               QP_CONTEXT_QPC_BYTES_156_RETRY_COUNT_INIT_S,
                               attr->retry_cnt);
-               roce_set_field(context->qpc_bytes_156,
-                              QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_M,
-                              QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_S,
-                              attr->timeout);
+               if (attr->timeout < 0x12) {
+                       dev_info(dev, "ack timeout value(0x%x) must bigger than 0x12.\n",
+                                attr->timeout);
+                       roce_set_field(context->qpc_bytes_156,
+                                      QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_M,
+                                      QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_S,
+                                      0x12);
+               } else {
+                       roce_set_field(context->qpc_bytes_156,
+                                      QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_M,
+                                      QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_S,
+                                      attr->timeout);
+               }
                roce_set_field(context->qpc_bytes_156,
                               QP_CONTEXT_QPC_BYTES_156_RNR_RETRY_COUNT_INIT_M,
                               QP_CONTEXT_QPC_BYTES_156_RNR_RETRY_COUNT_INIT_S,
@@ -2292,7 +2429,7 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                roce_set_field(context->qpc_bytes_156,
                               QP_CONTEXT_QPC_BYTES_156_PORT_NUM_M,
                               QP_CONTEXT_QPC_BYTES_156_PORT_NUM_S,
-                              hr_qp->port);
+                              hr_qp->phy_port);
                roce_set_field(context->qpc_bytes_156,
                               QP_CONTEXT_QPC_BYTES_156_SL_M,
                               QP_CONTEXT_QPC_BYTES_156_SL_S, attr->ah_attr.sl);
@@ -2357,21 +2494,15 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                               QP_CONTEXT_QPC_BYTES_188_TX_RETRY_CUR_INDEX_M,
                               QP_CONTEXT_QPC_BYTES_188_TX_RETRY_CUR_INDEX_S,
                               0);
-       } else if ((cur_state == IB_QPS_INIT && new_state == IB_QPS_RESET) ||
+       } else if (!((cur_state == IB_QPS_INIT && new_state == IB_QPS_RESET) ||
                   (cur_state == IB_QPS_INIT && new_state == IB_QPS_ERR) ||
                   (cur_state == IB_QPS_RTR && new_state == IB_QPS_RESET) ||
                   (cur_state == IB_QPS_RTR && new_state == IB_QPS_ERR) ||
                   (cur_state == IB_QPS_RTS && new_state == IB_QPS_RESET) ||
                   (cur_state == IB_QPS_RTS && new_state == IB_QPS_ERR) ||
                   (cur_state == IB_QPS_ERR && new_state == IB_QPS_RESET) ||
-                  (cur_state == IB_QPS_ERR && new_state == IB_QPS_ERR)) {
-               roce_set_field(context->qpc_bytes_144,
-                              QP_CONTEXT_QPC_BYTES_144_QP_STATE_M,
-                              QP_CONTEXT_QPC_BYTES_144_QP_STATE_S,
-                              attr->qp_state);
-
-       } else {
-               dev_err(dev, "not support this modify\n");
+                  (cur_state == IB_QPS_ERR && new_state == IB_QPS_ERR))) {
+               dev_err(dev, "not support this status migration\n");
                goto out;
        }
 
@@ -2397,43 +2528,32 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
        if (cur_state == IB_QPS_INIT && new_state == IB_QPS_INIT) {
                /* Memory barrier */
                wmb();
-               if (hr_qp->ibqp.qp_type == IB_QPT_GSI) {
-                       /* SW update GSI rq header */
-                       reg_val = roce_read(hr_dev, ROCEE_QP1C_CFG3_0_REG +
-                                           QP1C_CFGN_OFFSET * hr_qp->port);
-                       roce_set_field(reg_val,
-                                      ROCEE_QP1C_CFG3_0_ROCEE_QP1C_RQ_HEAD_M,
-                                      ROCEE_QP1C_CFG3_0_ROCEE_QP1C_RQ_HEAD_S,
-                                      hr_qp->rq.head);
-                       roce_write(hr_dev, ROCEE_QP1C_CFG3_0_REG +
-                                   QP1C_CFGN_OFFSET * hr_qp->port, reg_val);
-               } else {
-                       rq_db.u32_4 = 0;
-                       rq_db.u32_8 = 0;
-
-                       roce_set_field(rq_db.u32_4, RQ_DOORBELL_U32_4_RQ_HEAD_M,
-                                      RQ_DOORBELL_U32_4_RQ_HEAD_S,
-                                      hr_qp->rq.head);
-                       roce_set_field(rq_db.u32_8, RQ_DOORBELL_U32_8_QPN_M,
-                                      RQ_DOORBELL_U32_8_QPN_S, hr_qp->qpn);
-                       roce_set_field(rq_db.u32_8, RQ_DOORBELL_U32_8_CMD_M,
-                                      RQ_DOORBELL_U32_8_CMD_S, 1);
-                       roce_set_bit(rq_db.u32_8, RQ_DOORBELL_U32_8_HW_SYNC_S,
-                                    1);
 
-                       doorbell[0] = rq_db.u32_4;
-                       doorbell[1] = rq_db.u32_8;
-
-                       hns_roce_write64_k(doorbell, hr_qp->rq.db_reg_l);
+               roce_set_field(doorbell[0], RQ_DOORBELL_U32_4_RQ_HEAD_M,
+                              RQ_DOORBELL_U32_4_RQ_HEAD_S, hr_qp->rq.head);
+               roce_set_field(doorbell[1], RQ_DOORBELL_U32_8_QPN_M,
+                              RQ_DOORBELL_U32_8_QPN_S, hr_qp->qpn);
+               roce_set_field(doorbell[1], RQ_DOORBELL_U32_8_CMD_M,
+                              RQ_DOORBELL_U32_8_CMD_S, 1);
+               roce_set_bit(doorbell[1], RQ_DOORBELL_U32_8_HW_SYNC_S, 1);
+
+               if (ibqp->uobject) {
+                       hr_qp->rq.db_reg_l = hr_dev->reg_base +
+                                    ROCEE_DB_OTHERS_L_0_REG +
+                                    DB_REG_OFFSET * hr_dev->priv_uar.index;
                }
+
+               hns_roce_write64_k(doorbell, hr_qp->rq.db_reg_l);
        }
 
        hr_qp->state = new_state;
 
        if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)
                hr_qp->resp_depth = attr->max_dest_rd_atomic;
-       if (attr_mask & IB_QP_PORT)
-               hr_qp->port = (attr->port_num - 1);
+       if (attr_mask & IB_QP_PORT) {
+               hr_qp->port = attr->port_num - 1;
+               hr_qp->phy_port = hr_dev->iboe.phy_port[hr_qp->port];
+       }
 
        if (new_state == IB_QPS_RESET && !ibqp->uobject) {
                hns_roce_v1_cq_clean(to_hr_cq(ibqp->recv_cq), hr_qp->qpn,
@@ -2789,6 +2909,7 @@ struct hns_roce_hw hns_roce_hw_v1 = {
        .set_mtu = hns_roce_v1_set_mtu,
        .write_mtpt = hns_roce_v1_write_mtpt,
        .write_cqc = hns_roce_v1_write_cqc,
+       .clear_hem = hns_roce_v1_clear_hem,
        .modify_qp = hns_roce_v1_modify_qp,
        .query_qp = hns_roce_v1_query_qp,
        .destroy_qp = hns_roce_v1_destroy_qp,
index 316b592b1636df29be7939c4d7ca11abbdfce125..539b0a3b92b09a17dd663497c62b2607a8e94878 100644 (file)
 #define HNS_ROCE_V1_EXT_ODB_ALFUL      \
        (HNS_ROCE_V1_EXT_ODB_DEPTH - HNS_ROCE_V1_DB_RSVD)
 
+#define HNS_ROCE_BT_RSV_BUF_SIZE                       (1 << 17)
+
 #define HNS_ROCE_ODB_POLL_MODE                         0
 
 #define HNS_ROCE_SDB_NORMAL_MODE                       0
@@ -971,9 +973,16 @@ struct hns_roce_db_table {
        struct hns_roce_ext_db *ext_db;
 };
 
+struct hns_roce_bt_table {
+       struct hns_roce_buf_list qpc_buf;
+       struct hns_roce_buf_list mtpt_buf;
+       struct hns_roce_buf_list cqc_buf;
+};
+
 struct hns_roce_v1_priv {
        struct hns_roce_db_table  db_table;
        struct hns_roce_raq_table raq_table;
+       struct hns_roce_bt_table  bt_table;
 };
 
 int hns_dsaf_roce_reset(struct fwnode_handle *dsaf_fwnode, bool dereset);
index f64f0dde9a882c7ee697d990e4bf7acae6765610..764e35a54457e7c0c8bfde46577df1e3962d833c 100644 (file)
@@ -355,8 +355,7 @@ static int hns_roce_query_device(struct ib_device *ib_dev,
        props->max_qp = hr_dev->caps.num_qps;
        props->max_qp_wr = hr_dev->caps.max_wqes;
        props->device_cap_flags = IB_DEVICE_PORT_ACTIVE_EVENT |
-                                 IB_DEVICE_RC_RNR_NAK_GEN |
-                                 IB_DEVICE_LOCAL_DMA_LKEY;
+                                 IB_DEVICE_RC_RNR_NAK_GEN;
        props->max_sge = hr_dev->caps.max_sq_sg;
        props->max_sge_rd = 1;
        props->max_cq = hr_dev->caps.num_cqs;
@@ -372,6 +371,25 @@ static int hns_roce_query_device(struct ib_device *ib_dev,
        return 0;
 }
 
+static struct net_device *hns_roce_get_netdev(struct ib_device *ib_dev,
+                                             u8 port_num)
+{
+       struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev);
+       struct net_device *ndev;
+
+       if (port_num < 1 || port_num > hr_dev->caps.num_ports)
+               return NULL;
+
+       rcu_read_lock();
+
+       ndev = hr_dev->iboe.netdevs[port_num - 1];
+       if (ndev)
+               dev_hold(ndev);
+
+       rcu_read_unlock();
+       return ndev;
+}
+
 static int hns_roce_query_port(struct ib_device *ib_dev, u8 port_num,
                               struct ib_port_attr *props)
 {
@@ -584,6 +602,7 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
        struct device *dev = &hr_dev->pdev->dev;
 
        iboe = &hr_dev->iboe;
+       spin_lock_init(&iboe->lock);
 
        ib_dev = &hr_dev->ib_dev;
        strlcpy(ib_dev->name, "hisi_%d", IB_DEVICE_NAME_MAX);
@@ -618,6 +637,7 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
        ib_dev->query_port              = hns_roce_query_port;
        ib_dev->modify_port             = hns_roce_modify_port;
        ib_dev->get_link_layer          = hns_roce_get_link_layer;
+       ib_dev->get_netdev              = hns_roce_get_netdev;
        ib_dev->query_gid               = hns_roce_query_gid;
        ib_dev->query_pkey              = hns_roce_query_pkey;
        ib_dev->alloc_ucontext          = hns_roce_alloc_ucontext;
@@ -667,8 +687,6 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
                goto error_failed_setup_mtu_gids;
        }
 
-       spin_lock_init(&iboe->lock);
-
        iboe->nb.notifier_call = hns_roce_netdev_event;
        ret = register_netdevice_notifier(&iboe->nb);
        if (ret) {
@@ -777,6 +795,15 @@ static int hns_roce_get_cfg(struct hns_roce_dev *hr_dev)
        if (IS_ERR(hr_dev->reg_base))
                return PTR_ERR(hr_dev->reg_base);
 
+       /* read the node_guid of IB device from the DT or ACPI */
+       ret = device_property_read_u8_array(dev, "node-guid",
+                                           (u8 *)&hr_dev->ib_dev.node_guid,
+                                           GUID_LEN);
+       if (ret) {
+               dev_err(dev, "couldn't get node_guid from DT or ACPI!\n");
+               return ret;
+       }
+
        /* get the RoCE associated ethernet ports or netdevices */
        for (i = 0; i < HNS_ROCE_MAX_PORTS; i++) {
                if (dev_of_node(dev)) {
@@ -923,7 +950,6 @@ static int hns_roce_setup_hca(struct hns_roce_dev *hr_dev)
        struct device *dev = &hr_dev->pdev->dev;
 
        spin_lock_init(&hr_dev->sm_lock);
-       spin_lock_init(&hr_dev->cq_db_lock);
        spin_lock_init(&hr_dev->bt_cmd_lock);
 
        ret = hns_roce_init_uar_table(hr_dev);
index 59f5e2be046b326c7a3f0a8a2355f2ac33df4ab5..fb87883ead340423e087e14331487f735ec6e55f 100644 (file)
@@ -564,11 +564,14 @@ struct ib_mr *hns_roce_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
        if (mr->umem->page_size != HNS_ROCE_HEM_PAGE_SIZE) {
                dev_err(dev, "Just support 4K page size but is 0x%x now!\n",
                        mr->umem->page_size);
+               ret = -EINVAL;
+               goto err_umem;
        }
 
        if (n > HNS_ROCE_MAX_MTPT_PBL_NUM) {
                dev_err(dev, " MR len %lld err. MR is limited to 4G at most!\n",
                        length);
+               ret = -EINVAL;
                goto err_umem;
        }
 
index 16271b5bd1701deceaab511e9ad7d8813d555a6d..05db7d59812a6db5eb014a87e0d3566122442719 100644 (file)
 
 static int hns_roce_pd_alloc(struct hns_roce_dev *hr_dev, unsigned long *pdn)
 {
-       struct device *dev = &hr_dev->pdev->dev;
-       unsigned long pd_number;
-       int ret = 0;
-
-       ret = hns_roce_bitmap_alloc(&hr_dev->pd_bitmap, &pd_number);
-       if (ret == -1) {
-               dev_err(dev, "alloc pdn from pdbitmap failed\n");
-               return -ENOMEM;
-       }
-
-       *pdn = pd_number;
-
-       return 0;
+       return hns_roce_bitmap_alloc(&hr_dev->pd_bitmap, pdn);
 }
 
 static void hns_roce_pd_free(struct hns_roce_dev *hr_dev, unsigned long pdn)
@@ -117,9 +105,15 @@ int hns_roce_uar_alloc(struct hns_roce_dev *hr_dev, struct hns_roce_uar *uar)
        if (ret == -1)
                return -ENOMEM;
 
-       uar->index = (uar->index - 1) % hr_dev->caps.phy_num_uars + 1;
+       if (uar->index > 0)
+               uar->index = (uar->index - 1) %
+                            (hr_dev->caps.phy_num_uars - 1) + 1;
 
        res = platform_get_resource(hr_dev->pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&hr_dev->pdev->dev, "memory resource not found!\n");
+               return -EINVAL;
+       }
        uar->pfn = ((res->start) >> PAGE_SHIFT) + uar->index;
 
        return 0;
index 645c18d809a5de94bfb2120caca67efda242e928..e86dd8d0677760c4aa38432405b9dd977b35c610 100644 (file)
  */
 
 #include <linux/platform_device.h>
+#include <rdma/ib_addr.h>
 #include <rdma/ib_umem.h>
 #include "hns_roce_common.h"
 #include "hns_roce_device.h"
 #include "hns_roce_hem.h"
 #include "hns_roce_user.h"
 
-#define DB_REG_OFFSET                  0x1000
-#define SQP_NUM                                12
+#define SQP_NUM                                (2 * HNS_ROCE_MAX_PORTS)
 
 void hns_roce_qp_event(struct hns_roce_dev *hr_dev, u32 qpn, int event_type)
 {
@@ -113,16 +113,8 @@ static int hns_roce_reserve_range_qp(struct hns_roce_dev *hr_dev, int cnt,
                                     int align, unsigned long *base)
 {
        struct hns_roce_qp_table *qp_table = &hr_dev->qp_table;
-       int ret = 0;
-       unsigned long qpn;
-
-       ret = hns_roce_bitmap_alloc_range(&qp_table->bitmap, cnt, align, &qpn);
-       if (ret == -1)
-               return -ENOMEM;
-
-       *base = qpn;
 
-       return 0;
+       return hns_roce_bitmap_alloc_range(&qp_table->bitmap, cnt, align, base);
 }
 
 enum hns_roce_qp_state to_hns_roce_state(enum ib_qp_state state)
@@ -255,7 +247,7 @@ void hns_roce_release_range_qp(struct hns_roce_dev *hr_dev, int base_qpn,
 {
        struct hns_roce_qp_table *qp_table = &hr_dev->qp_table;
 
-       if (base_qpn < (hr_dev->caps.sqp_start + 2 * hr_dev->caps.num_ports))
+       if (base_qpn < SQP_NUM)
                return;
 
        hns_roce_bitmap_free_range(&qp_table->bitmap, base_qpn, cnt);
@@ -345,12 +337,10 @@ static int hns_roce_set_user_sq_size(struct hns_roce_dev *hr_dev,
 
 static int hns_roce_set_kernel_sq_size(struct hns_roce_dev *hr_dev,
                                       struct ib_qp_cap *cap,
-                                      enum ib_qp_type type,
                                       struct hns_roce_qp *hr_qp)
 {
        struct device *dev = &hr_dev->pdev->dev;
        u32 max_cnt;
-       (void)type;
 
        if (cap->max_send_wr  > hr_dev->caps.max_wqes  ||
            cap->max_send_sge > hr_dev->caps.max_sq_sg ||
@@ -476,7 +466,7 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
 
                /* Set SQ size */
                ret = hns_roce_set_kernel_sq_size(hr_dev, &init_attr->cap,
-                                                 init_attr->qp_type, hr_qp);
+                                                 hr_qp);
                if (ret) {
                        dev_err(dev, "hns_roce_set_kernel_sq_size error!\n");
                        goto err_out;
@@ -617,21 +607,19 @@ struct ib_qp *hns_roce_create_qp(struct ib_pd *pd,
                        return ERR_PTR(-ENOMEM);
 
                hr_qp = &hr_sqp->hr_qp;
+               hr_qp->port = init_attr->port_num - 1;
+               hr_qp->phy_port = hr_dev->iboe.phy_port[hr_qp->port];
+               hr_qp->ibqp.qp_num = HNS_ROCE_MAX_PORTS +
+                                    hr_dev->iboe.phy_port[hr_qp->port];
 
                ret = hns_roce_create_qp_common(hr_dev, pd, init_attr, udata,
-                                               hr_dev->caps.sqp_start +
-                                               hr_dev->caps.num_ports +
-                                               init_attr->port_num - 1, hr_qp);
+                                               hr_qp->ibqp.qp_num, hr_qp);
                if (ret) {
                        dev_err(dev, "Create GSI QP failed!\n");
                        kfree(hr_sqp);
                        return ERR_PTR(ret);
                }
 
-               hr_qp->port = (init_attr->port_num - 1);
-               hr_qp->ibqp.qp_num = hr_dev->caps.sqp_start +
-                                    hr_dev->caps.num_ports +
-                                    init_attr->port_num - 1;
                break;
        }
        default:{
@@ -670,6 +658,7 @@ int hns_roce_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
        struct device *dev = &hr_dev->pdev->dev;
        int ret = -EINVAL;
        int p;
+       enum ib_mtu active_mtu;
 
        mutex_lock(&hr_qp->mutex);
 
@@ -700,6 +689,19 @@ int hns_roce_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                }
        }
 
+       if (attr_mask & IB_QP_PATH_MTU) {
+               p = attr_mask & IB_QP_PORT ? (attr->port_num - 1) : hr_qp->port;
+               active_mtu = iboe_get_mtu(hr_dev->iboe.netdevs[p]->mtu);
+
+               if (attr->path_mtu > IB_MTU_2048 ||
+                   attr->path_mtu < IB_MTU_256 ||
+                   attr->path_mtu > active_mtu) {
+                       dev_err(dev, "attr path_mtu(%d)invalid while modify qp",
+                               attr->path_mtu);
+                       goto out;
+               }
+       }
+
        if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC &&
            attr->max_rd_atomic > hr_dev->caps.max_qp_init_rdma) {
                dev_err(dev, "attr max_rd_atomic invalid.attr->max_rd_atomic=%d\n",
@@ -782,29 +784,11 @@ static void *get_wqe(struct hns_roce_qp *hr_qp, int offset)
 
 void *get_recv_wqe(struct hns_roce_qp *hr_qp, int n)
 {
-       struct ib_qp *ibqp = &hr_qp->ibqp;
-       struct hns_roce_dev *hr_dev = to_hr_dev(ibqp->device);
-
-       if ((n < 0) || (n > hr_qp->rq.wqe_cnt)) {
-               dev_err(&hr_dev->pdev->dev, "rq wqe index:%d,rq wqe cnt:%d\r\n",
-                       n, hr_qp->rq.wqe_cnt);
-               return NULL;
-       }
-
        return get_wqe(hr_qp, hr_qp->rq.offset + (n << hr_qp->rq.wqe_shift));
 }
 
 void *get_send_wqe(struct hns_roce_qp *hr_qp, int n)
 {
-       struct ib_qp *ibqp = &hr_qp->ibqp;
-       struct hns_roce_dev *hr_dev = to_hr_dev(ibqp->device);
-
-       if ((n < 0) || (n > hr_qp->sq.wqe_cnt)) {
-               dev_err(&hr_dev->pdev->dev, "sq wqe index:%d,sq wqe cnt:%d\r\n",
-                       n, hr_qp->sq.wqe_cnt);
-               return NULL;
-       }
-
        return get_wqe(hr_qp, hr_qp->sq.offset + (n << hr_qp->sq.wqe_shift));
 }
 
@@ -837,8 +821,7 @@ int hns_roce_init_qp_table(struct hns_roce_dev *hr_dev)
 
        /* A port include two SQP, six port total 12 */
        ret = hns_roce_bitmap_init(&qp_table->bitmap, hr_dev->caps.num_qps,
-                                  hr_dev->caps.num_qps - 1,
-                                  hr_dev->caps.sqp_start + SQP_NUM,
+                                  hr_dev->caps.num_qps - 1, SQP_NUM,
                                   reserved_from_top);
        if (ret) {
                dev_err(&hr_dev->pdev->dev, "qp bitmap init failed!error=%d\n",
index 6c00d04b8b2852369f4f43d78c39c68898ff4640..c6fe89d79248fa28d05282440ede6f67cd35e75c 100644 (file)
@@ -472,7 +472,7 @@ int mthca_map_user_db(struct mthca_dev *dev, struct mthca_uar *uar,
                goto out;
        }
 
-       ret = get_user_pages(uaddr & PAGE_MASK, 1, 1, 0, pages, NULL);
+       ret = get_user_pages(uaddr & PAGE_MASK, 1, FOLL_WRITE, pages, NULL);
        if (ret < 0)
                goto out;
 
diff --git a/drivers/infiniband/hw/qedr/Kconfig b/drivers/infiniband/hw/qedr/Kconfig
new file mode 100644 (file)
index 0000000..7c06d85
--- /dev/null
@@ -0,0 +1,7 @@
+config INFINIBAND_QEDR
+       tristate "QLogic RoCE driver"
+       depends on 64BIT && QEDE
+       select QED_LL2
+       ---help---
+         This driver provides low-level InfiniBand over Ethernet
+         support for QLogic QED host channel adapters (HCAs).
diff --git a/drivers/infiniband/hw/qedr/Makefile b/drivers/infiniband/hw/qedr/Makefile
new file mode 100644 (file)
index 0000000..ba7067c
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_INFINIBAND_QEDR) := qedr.o
+
+qedr-y := main.o verbs.o qedr_cm.o
diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c
new file mode 100644 (file)
index 0000000..7b74d09
--- /dev/null
@@ -0,0 +1,914 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed 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, or the
+ * OpenIB.org BSD license below:
+ *
+ *     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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/module.h>
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_addr.h>
+#include <rdma/ib_user_verbs.h>
+#include <linux/netdevice.h>
+#include <linux/iommu.h>
+#include <net/addrconf.h>
+#include <linux/qed/qede_roce.h>
+#include <linux/qed/qed_chain.h>
+#include <linux/qed/qed_if.h>
+#include "qedr.h"
+#include "verbs.h"
+#include <rdma/qedr-abi.h>
+
+MODULE_DESCRIPTION("QLogic 40G/100G ROCE Driver");
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(QEDR_MODULE_VERSION);
+
+#define QEDR_WQ_MULTIPLIER_DFT (3)
+
+void qedr_ib_dispatch_event(struct qedr_dev *dev, u8 port_num,
+                           enum ib_event_type type)
+{
+       struct ib_event ibev;
+
+       ibev.device = &dev->ibdev;
+       ibev.element.port_num = port_num;
+       ibev.event = type;
+
+       ib_dispatch_event(&ibev);
+}
+
+static enum rdma_link_layer qedr_link_layer(struct ib_device *device,
+                                           u8 port_num)
+{
+       return IB_LINK_LAYER_ETHERNET;
+}
+
+static void qedr_get_dev_fw_str(struct ib_device *ibdev, char *str,
+                               size_t str_len)
+{
+       struct qedr_dev *qedr = get_qedr_dev(ibdev);
+       u32 fw_ver = (u32)qedr->attr.fw_ver;
+
+       snprintf(str, str_len, "%d. %d. %d. %d",
+                (fw_ver >> 24) & 0xFF, (fw_ver >> 16) & 0xFF,
+                (fw_ver >> 8) & 0xFF, fw_ver & 0xFF);
+}
+
+static struct net_device *qedr_get_netdev(struct ib_device *dev, u8 port_num)
+{
+       struct qedr_dev *qdev;
+
+       qdev = get_qedr_dev(dev);
+       dev_hold(qdev->ndev);
+
+       /* The HW vendor's device driver must guarantee
+        * that this function returns NULL before the net device reaches
+        * NETDEV_UNREGISTER_FINAL state.
+        */
+       return qdev->ndev;
+}
+
+static int qedr_register_device(struct qedr_dev *dev)
+{
+       strlcpy(dev->ibdev.name, "qedr%d", IB_DEVICE_NAME_MAX);
+
+       dev->ibdev.node_guid = dev->attr.node_guid;
+       memcpy(dev->ibdev.node_desc, QEDR_NODE_DESC, sizeof(QEDR_NODE_DESC));
+       dev->ibdev.owner = THIS_MODULE;
+       dev->ibdev.uverbs_abi_ver = QEDR_ABI_VERSION;
+
+       dev->ibdev.uverbs_cmd_mask = QEDR_UVERBS(GET_CONTEXT) |
+                                    QEDR_UVERBS(QUERY_DEVICE) |
+                                    QEDR_UVERBS(QUERY_PORT) |
+                                    QEDR_UVERBS(ALLOC_PD) |
+                                    QEDR_UVERBS(DEALLOC_PD) |
+                                    QEDR_UVERBS(CREATE_COMP_CHANNEL) |
+                                    QEDR_UVERBS(CREATE_CQ) |
+                                    QEDR_UVERBS(RESIZE_CQ) |
+                                    QEDR_UVERBS(DESTROY_CQ) |
+                                    QEDR_UVERBS(REQ_NOTIFY_CQ) |
+                                    QEDR_UVERBS(CREATE_QP) |
+                                    QEDR_UVERBS(MODIFY_QP) |
+                                    QEDR_UVERBS(QUERY_QP) |
+                                    QEDR_UVERBS(DESTROY_QP) |
+                                    QEDR_UVERBS(REG_MR) |
+                                    QEDR_UVERBS(DEREG_MR) |
+                                    QEDR_UVERBS(POLL_CQ) |
+                                    QEDR_UVERBS(POST_SEND) |
+                                    QEDR_UVERBS(POST_RECV);
+
+       dev->ibdev.phys_port_cnt = 1;
+       dev->ibdev.num_comp_vectors = dev->num_cnq;
+       dev->ibdev.node_type = RDMA_NODE_IB_CA;
+
+       dev->ibdev.query_device = qedr_query_device;
+       dev->ibdev.query_port = qedr_query_port;
+       dev->ibdev.modify_port = qedr_modify_port;
+
+       dev->ibdev.query_gid = qedr_query_gid;
+       dev->ibdev.add_gid = qedr_add_gid;
+       dev->ibdev.del_gid = qedr_del_gid;
+
+       dev->ibdev.alloc_ucontext = qedr_alloc_ucontext;
+       dev->ibdev.dealloc_ucontext = qedr_dealloc_ucontext;
+       dev->ibdev.mmap = qedr_mmap;
+
+       dev->ibdev.alloc_pd = qedr_alloc_pd;
+       dev->ibdev.dealloc_pd = qedr_dealloc_pd;
+
+       dev->ibdev.create_cq = qedr_create_cq;
+       dev->ibdev.destroy_cq = qedr_destroy_cq;
+       dev->ibdev.resize_cq = qedr_resize_cq;
+       dev->ibdev.req_notify_cq = qedr_arm_cq;
+
+       dev->ibdev.create_qp = qedr_create_qp;
+       dev->ibdev.modify_qp = qedr_modify_qp;
+       dev->ibdev.query_qp = qedr_query_qp;
+       dev->ibdev.destroy_qp = qedr_destroy_qp;
+
+       dev->ibdev.query_pkey = qedr_query_pkey;
+
+       dev->ibdev.create_ah = qedr_create_ah;
+       dev->ibdev.destroy_ah = qedr_destroy_ah;
+
+       dev->ibdev.get_dma_mr = qedr_get_dma_mr;
+       dev->ibdev.dereg_mr = qedr_dereg_mr;
+       dev->ibdev.reg_user_mr = qedr_reg_user_mr;
+       dev->ibdev.alloc_mr = qedr_alloc_mr;
+       dev->ibdev.map_mr_sg = qedr_map_mr_sg;
+
+       dev->ibdev.poll_cq = qedr_poll_cq;
+       dev->ibdev.post_send = qedr_post_send;
+       dev->ibdev.post_recv = qedr_post_recv;
+
+       dev->ibdev.process_mad = qedr_process_mad;
+       dev->ibdev.get_port_immutable = qedr_port_immutable;
+       dev->ibdev.get_netdev = qedr_get_netdev;
+
+       dev->ibdev.dma_device = &dev->pdev->dev;
+
+       dev->ibdev.get_link_layer = qedr_link_layer;
+       dev->ibdev.get_dev_fw_str = qedr_get_dev_fw_str;
+
+       return ib_register_device(&dev->ibdev, NULL);
+}
+
+/* This function allocates fast-path status block memory */
+static int qedr_alloc_mem_sb(struct qedr_dev *dev,
+                            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(&dev->pdev->dev,
+                                    sizeof(*sb_virt), &sb_phys, GFP_KERNEL);
+       if (!sb_virt)
+               return -ENOMEM;
+
+       rc = dev->ops->common->sb_init(dev->cdev, sb_info,
+                                      sb_virt, sb_phys, sb_id,
+                                      QED_SB_TYPE_CNQ);
+       if (rc) {
+               pr_err("Status block initialization failed\n");
+               dma_free_coherent(&dev->pdev->dev, sizeof(*sb_virt),
+                                 sb_virt, sb_phys);
+               return rc;
+       }
+
+       return 0;
+}
+
+static void qedr_free_mem_sb(struct qedr_dev *dev,
+                            struct qed_sb_info *sb_info, int sb_id)
+{
+       if (sb_info->sb_virt) {
+               dev->ops->common->sb_release(dev->cdev, sb_info, sb_id);
+               dma_free_coherent(&dev->pdev->dev, sizeof(*sb_info->sb_virt),
+                                 (void *)sb_info->sb_virt, sb_info->sb_phys);
+       }
+}
+
+static void qedr_free_resources(struct qedr_dev *dev)
+{
+       int i;
+
+       for (i = 0; i < dev->num_cnq; i++) {
+               qedr_free_mem_sb(dev, &dev->sb_array[i], dev->sb_start + i);
+               dev->ops->common->chain_free(dev->cdev, &dev->cnq_array[i].pbl);
+       }
+
+       kfree(dev->cnq_array);
+       kfree(dev->sb_array);
+       kfree(dev->sgid_tbl);
+}
+
+static int qedr_alloc_resources(struct qedr_dev *dev)
+{
+       struct qedr_cnq *cnq;
+       __le16 *cons_pi;
+       u16 n_entries;
+       int i, rc;
+
+       dev->sgid_tbl = kzalloc(sizeof(union ib_gid) *
+                               QEDR_MAX_SGID, GFP_KERNEL);
+       if (!dev->sgid_tbl)
+               return -ENOMEM;
+
+       spin_lock_init(&dev->sgid_lock);
+
+       /* Allocate Status blocks for CNQ */
+       dev->sb_array = kcalloc(dev->num_cnq, sizeof(*dev->sb_array),
+                               GFP_KERNEL);
+       if (!dev->sb_array) {
+               rc = -ENOMEM;
+               goto err1;
+       }
+
+       dev->cnq_array = kcalloc(dev->num_cnq,
+                                sizeof(*dev->cnq_array), GFP_KERNEL);
+       if (!dev->cnq_array) {
+               rc = -ENOMEM;
+               goto err2;
+       }
+
+       dev->sb_start = dev->ops->rdma_get_start_sb(dev->cdev);
+
+       /* Allocate CNQ PBLs */
+       n_entries = min_t(u32, QED_RDMA_MAX_CNQ_SIZE, QEDR_ROCE_MAX_CNQ_SIZE);
+       for (i = 0; i < dev->num_cnq; i++) {
+               cnq = &dev->cnq_array[i];
+
+               rc = qedr_alloc_mem_sb(dev, &dev->sb_array[i],
+                                      dev->sb_start + i);
+               if (rc)
+                       goto err3;
+
+               rc = dev->ops->common->chain_alloc(dev->cdev,
+                                                  QED_CHAIN_USE_TO_CONSUME,
+                                                  QED_CHAIN_MODE_PBL,
+                                                  QED_CHAIN_CNT_TYPE_U16,
+                                                  n_entries,
+                                                  sizeof(struct regpair *),
+                                                  &cnq->pbl);
+               if (rc)
+                       goto err4;
+
+               cnq->dev = dev;
+               cnq->sb = &dev->sb_array[i];
+               cons_pi = dev->sb_array[i].sb_virt->pi_array;
+               cnq->hw_cons_ptr = &cons_pi[QED_ROCE_PROTOCOL_INDEX];
+               cnq->index = i;
+               sprintf(cnq->name, "qedr%d@pci:%s", i, pci_name(dev->pdev));
+
+               DP_DEBUG(dev, QEDR_MSG_INIT, "cnq[%d].cons=%d\n",
+                        i, qed_chain_get_cons_idx(&cnq->pbl));
+       }
+
+       return 0;
+err4:
+       qedr_free_mem_sb(dev, &dev->sb_array[i], dev->sb_start + i);
+err3:
+       for (--i; i >= 0; i--) {
+               dev->ops->common->chain_free(dev->cdev, &dev->cnq_array[i].pbl);
+               qedr_free_mem_sb(dev, &dev->sb_array[i], dev->sb_start + i);
+       }
+       kfree(dev->cnq_array);
+err2:
+       kfree(dev->sb_array);
+err1:
+       kfree(dev->sgid_tbl);
+       return rc;
+}
+
+/* QEDR sysfs interface */
+static ssize_t show_rev(struct device *device, struct device_attribute *attr,
+                       char *buf)
+{
+       struct qedr_dev *dev = dev_get_drvdata(device);
+
+       return scnprintf(buf, PAGE_SIZE, "0x%x\n", dev->pdev->vendor);
+}
+
+static ssize_t show_hca_type(struct device *device,
+                            struct device_attribute *attr, char *buf)
+{
+       return scnprintf(buf, PAGE_SIZE, "%s\n", "HCA_TYPE_TO_SET");
+}
+
+static DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL);
+static DEVICE_ATTR(hca_type, S_IRUGO, show_hca_type, NULL);
+
+static struct device_attribute *qedr_attributes[] = {
+       &dev_attr_hw_rev,
+       &dev_attr_hca_type
+};
+
+static void qedr_remove_sysfiles(struct qedr_dev *dev)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(qedr_attributes); i++)
+               device_remove_file(&dev->ibdev.dev, qedr_attributes[i]);
+}
+
+static void qedr_pci_set_atomic(struct qedr_dev *dev, struct pci_dev *pdev)
+{
+       struct pci_dev *bridge;
+       u32 val;
+
+       dev->atomic_cap = IB_ATOMIC_NONE;
+
+       bridge = pdev->bus->self;
+       if (!bridge)
+               return;
+
+       /* Check whether we are connected directly or via a switch */
+       while (bridge && bridge->bus->parent) {
+               DP_DEBUG(dev, QEDR_MSG_INIT,
+                        "Device is not connected directly to root. bridge->bus->number=%d primary=%d\n",
+                        bridge->bus->number, bridge->bus->primary);
+               /* Need to check Atomic Op Routing Supported all the way to
+                * root complex.
+                */
+               pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &val);
+               if (!(val & PCI_EXP_DEVCAP2_ATOMIC_ROUTE)) {
+                       pcie_capability_clear_word(pdev,
+                                                  PCI_EXP_DEVCTL2,
+                                                  PCI_EXP_DEVCTL2_ATOMIC_REQ);
+                       return;
+               }
+               bridge = bridge->bus->parent->self;
+       }
+       bridge = pdev->bus->self;
+
+       /* according to bridge capability */
+       pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &val);
+       if (val & PCI_EXP_DEVCAP2_ATOMIC_COMP64) {
+               pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2,
+                                        PCI_EXP_DEVCTL2_ATOMIC_REQ);
+               dev->atomic_cap = IB_ATOMIC_GLOB;
+       } else {
+               pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL2,
+                                          PCI_EXP_DEVCTL2_ATOMIC_REQ);
+       }
+}
+
+static const struct qed_rdma_ops *qed_ops;
+
+#define HILO_U64(hi, lo)               ((((u64)(hi)) << 32) + (lo))
+
+static irqreturn_t qedr_irq_handler(int irq, void *handle)
+{
+       u16 hw_comp_cons, sw_comp_cons;
+       struct qedr_cnq *cnq = handle;
+       struct regpair *cq_handle;
+       struct qedr_cq *cq;
+
+       qed_sb_ack(cnq->sb, IGU_INT_DISABLE, 0);
+
+       qed_sb_update_sb_idx(cnq->sb);
+
+       hw_comp_cons = le16_to_cpu(*cnq->hw_cons_ptr);
+       sw_comp_cons = qed_chain_get_cons_idx(&cnq->pbl);
+
+       /* Align protocol-index and chain reads */
+       rmb();
+
+       while (sw_comp_cons != hw_comp_cons) {
+               cq_handle = (struct regpair *)qed_chain_consume(&cnq->pbl);
+               cq = (struct qedr_cq *)(uintptr_t)HILO_U64(cq_handle->hi,
+                               cq_handle->lo);
+
+               if (cq == NULL) {
+                       DP_ERR(cnq->dev,
+                              "Received NULL CQ cq_handle->hi=%d cq_handle->lo=%d sw_comp_cons=%d hw_comp_cons=%d\n",
+                              cq_handle->hi, cq_handle->lo, sw_comp_cons,
+                              hw_comp_cons);
+
+                       break;
+               }
+
+               if (cq->sig != QEDR_CQ_MAGIC_NUMBER) {
+                       DP_ERR(cnq->dev,
+                              "Problem with cq signature, cq_handle->hi=%d ch_handle->lo=%d cq=%p\n",
+                              cq_handle->hi, cq_handle->lo, cq);
+                       break;
+               }
+
+               cq->arm_flags = 0;
+
+               if (cq->ibcq.comp_handler)
+                       (*cq->ibcq.comp_handler)
+                               (&cq->ibcq, cq->ibcq.cq_context);
+
+               sw_comp_cons = qed_chain_get_cons_idx(&cnq->pbl);
+
+               cnq->n_comp++;
+
+       }
+
+       qed_ops->rdma_cnq_prod_update(cnq->dev->rdma_ctx, cnq->index,
+                                     sw_comp_cons);
+
+       qed_sb_ack(cnq->sb, IGU_INT_ENABLE, 1);
+
+       return IRQ_HANDLED;
+}
+
+static void qedr_sync_free_irqs(struct qedr_dev *dev)
+{
+       u32 vector;
+       int i;
+
+       for (i = 0; i < dev->int_info.used_cnt; i++) {
+               if (dev->int_info.msix_cnt) {
+                       vector = dev->int_info.msix[i * dev->num_hwfns].vector;
+                       synchronize_irq(vector);
+                       free_irq(vector, &dev->cnq_array[i]);
+               }
+       }
+
+       dev->int_info.used_cnt = 0;
+}
+
+static int qedr_req_msix_irqs(struct qedr_dev *dev)
+{
+       int i, rc = 0;
+
+       if (dev->num_cnq > dev->int_info.msix_cnt) {
+               DP_ERR(dev,
+                      "Interrupt mismatch: %d CNQ queues > %d MSI-x vectors\n",
+                      dev->num_cnq, dev->int_info.msix_cnt);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < dev->num_cnq; i++) {
+               rc = request_irq(dev->int_info.msix[i * dev->num_hwfns].vector,
+                                qedr_irq_handler, 0, dev->cnq_array[i].name,
+                                &dev->cnq_array[i]);
+               if (rc) {
+                       DP_ERR(dev, "Request cnq %d irq failed\n", i);
+                       qedr_sync_free_irqs(dev);
+               } else {
+                       DP_DEBUG(dev, QEDR_MSG_INIT,
+                                "Requested cnq irq for %s [entry %d]. Cookie is at %p\n",
+                                dev->cnq_array[i].name, i,
+                                &dev->cnq_array[i]);
+                       dev->int_info.used_cnt++;
+               }
+       }
+
+       return rc;
+}
+
+static int qedr_setup_irqs(struct qedr_dev *dev)
+{
+       int rc;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "qedr_setup_irqs\n");
+
+       /* Learn Interrupt configuration */
+       rc = dev->ops->rdma_set_rdma_int(dev->cdev, dev->num_cnq);
+       if (rc < 0)
+               return rc;
+
+       rc = dev->ops->rdma_get_rdma_int(dev->cdev, &dev->int_info);
+       if (rc) {
+               DP_DEBUG(dev, QEDR_MSG_INIT, "get_rdma_int failed\n");
+               return rc;
+       }
+
+       if (dev->int_info.msix_cnt) {
+               DP_DEBUG(dev, QEDR_MSG_INIT, "rdma msix_cnt = %d\n",
+                        dev->int_info.msix_cnt);
+               rc = qedr_req_msix_irqs(dev);
+               if (rc)
+                       return rc;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "qedr_setup_irqs succeeded\n");
+
+       return 0;
+}
+
+static int qedr_set_device_attr(struct qedr_dev *dev)
+{
+       struct qed_rdma_device *qed_attr;
+       struct qedr_device_attr *attr;
+       u32 page_size;
+
+       /* Part 1 - query core capabilities */
+       qed_attr = dev->ops->rdma_query_device(dev->rdma_ctx);
+
+       /* Part 2 - check capabilities */
+       page_size = ~dev->attr.page_size_caps + 1;
+       if (page_size > PAGE_SIZE) {
+               DP_ERR(dev,
+                      "Kernel PAGE_SIZE is %ld which is smaller than minimum page size (%d) required by qedr\n",
+                      PAGE_SIZE, page_size);
+               return -ENODEV;
+       }
+
+       /* Part 3 - copy and update capabilities */
+       attr = &dev->attr;
+       attr->vendor_id = qed_attr->vendor_id;
+       attr->vendor_part_id = qed_attr->vendor_part_id;
+       attr->hw_ver = qed_attr->hw_ver;
+       attr->fw_ver = qed_attr->fw_ver;
+       attr->node_guid = qed_attr->node_guid;
+       attr->sys_image_guid = qed_attr->sys_image_guid;
+       attr->max_cnq = qed_attr->max_cnq;
+       attr->max_sge = qed_attr->max_sge;
+       attr->max_inline = qed_attr->max_inline;
+       attr->max_sqe = min_t(u32, qed_attr->max_wqe, QEDR_MAX_SQE);
+       attr->max_rqe = min_t(u32, qed_attr->max_wqe, QEDR_MAX_RQE);
+       attr->max_qp_resp_rd_atomic_resc = qed_attr->max_qp_resp_rd_atomic_resc;
+       attr->max_qp_req_rd_atomic_resc = qed_attr->max_qp_req_rd_atomic_resc;
+       attr->max_dev_resp_rd_atomic_resc =
+           qed_attr->max_dev_resp_rd_atomic_resc;
+       attr->max_cq = qed_attr->max_cq;
+       attr->max_qp = qed_attr->max_qp;
+       attr->max_mr = qed_attr->max_mr;
+       attr->max_mr_size = qed_attr->max_mr_size;
+       attr->max_cqe = min_t(u64, qed_attr->max_cqe, QEDR_MAX_CQES);
+       attr->max_mw = qed_attr->max_mw;
+       attr->max_fmr = qed_attr->max_fmr;
+       attr->max_mr_mw_fmr_pbl = qed_attr->max_mr_mw_fmr_pbl;
+       attr->max_mr_mw_fmr_size = qed_attr->max_mr_mw_fmr_size;
+       attr->max_pd = qed_attr->max_pd;
+       attr->max_ah = qed_attr->max_ah;
+       attr->max_pkey = qed_attr->max_pkey;
+       attr->max_srq = qed_attr->max_srq;
+       attr->max_srq_wr = qed_attr->max_srq_wr;
+       attr->dev_caps = qed_attr->dev_caps;
+       attr->page_size_caps = qed_attr->page_size_caps;
+       attr->dev_ack_delay = qed_attr->dev_ack_delay;
+       attr->reserved_lkey = qed_attr->reserved_lkey;
+       attr->bad_pkey_counter = qed_attr->bad_pkey_counter;
+       attr->max_stats_queues = qed_attr->max_stats_queues;
+
+       return 0;
+}
+
+void qedr_unaffiliated_event(void *context,
+                            u8 event_code)
+{
+       pr_err("unaffiliated event not implemented yet\n");
+}
+
+void qedr_affiliated_event(void *context, u8 e_code, void *fw_handle)
+{
+#define EVENT_TYPE_NOT_DEFINED 0
+#define EVENT_TYPE_CQ          1
+#define EVENT_TYPE_QP          2
+       struct qedr_dev *dev = (struct qedr_dev *)context;
+       union event_ring_data *data = fw_handle;
+       u64 roce_handle64 = ((u64)data->roce_handle.hi << 32) +
+                           data->roce_handle.lo;
+       u8 event_type = EVENT_TYPE_NOT_DEFINED;
+       struct ib_event event;
+       struct ib_cq *ibcq;
+       struct ib_qp *ibqp;
+       struct qedr_cq *cq;
+       struct qedr_qp *qp;
+
+       switch (e_code) {
+       case ROCE_ASYNC_EVENT_CQ_OVERFLOW_ERR:
+               event.event = IB_EVENT_CQ_ERR;
+               event_type = EVENT_TYPE_CQ;
+               break;
+       case ROCE_ASYNC_EVENT_SQ_DRAINED:
+               event.event = IB_EVENT_SQ_DRAINED;
+               event_type = EVENT_TYPE_QP;
+               break;
+       case ROCE_ASYNC_EVENT_QP_CATASTROPHIC_ERR:
+               event.event = IB_EVENT_QP_FATAL;
+               event_type = EVENT_TYPE_QP;
+               break;
+       case ROCE_ASYNC_EVENT_LOCAL_INVALID_REQUEST_ERR:
+               event.event = IB_EVENT_QP_REQ_ERR;
+               event_type = EVENT_TYPE_QP;
+               break;
+       case ROCE_ASYNC_EVENT_LOCAL_ACCESS_ERR:
+               event.event = IB_EVENT_QP_ACCESS_ERR;
+               event_type = EVENT_TYPE_QP;
+               break;
+       default:
+               DP_ERR(dev, "unsupported event %d on handle=%llx\n", e_code,
+                      roce_handle64);
+       }
+
+       switch (event_type) {
+       case EVENT_TYPE_CQ:
+               cq = (struct qedr_cq *)(uintptr_t)roce_handle64;
+               if (cq) {
+                       ibcq = &cq->ibcq;
+                       if (ibcq->event_handler) {
+                               event.device = ibcq->device;
+                               event.element.cq = ibcq;
+                               ibcq->event_handler(&event, ibcq->cq_context);
+                       }
+               } else {
+                       WARN(1,
+                            "Error: CQ event with NULL pointer ibcq. Handle=%llx\n",
+                            roce_handle64);
+               }
+               DP_ERR(dev, "CQ event %d on hanlde %p\n", e_code, cq);
+               break;
+       case EVENT_TYPE_QP:
+               qp = (struct qedr_qp *)(uintptr_t)roce_handle64;
+               if (qp) {
+                       ibqp = &qp->ibqp;
+                       if (ibqp->event_handler) {
+                               event.device = ibqp->device;
+                               event.element.qp = ibqp;
+                               ibqp->event_handler(&event, ibqp->qp_context);
+                       }
+               } else {
+                       WARN(1,
+                            "Error: QP event with NULL pointer ibqp. Handle=%llx\n",
+                            roce_handle64);
+               }
+               DP_ERR(dev, "QP event %d on hanlde %p\n", e_code, qp);
+               break;
+       default:
+               break;
+       }
+}
+
+static int qedr_init_hw(struct qedr_dev *dev)
+{
+       struct qed_rdma_add_user_out_params out_params;
+       struct qed_rdma_start_in_params *in_params;
+       struct qed_rdma_cnq_params *cur_pbl;
+       struct qed_rdma_events events;
+       dma_addr_t p_phys_table;
+       u32 page_cnt;
+       int rc = 0;
+       int i;
+
+       in_params =  kzalloc(sizeof(*in_params), GFP_KERNEL);
+       if (!in_params) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       in_params->desired_cnq = dev->num_cnq;
+       for (i = 0; i < dev->num_cnq; i++) {
+               cur_pbl = &in_params->cnq_pbl_list[i];
+
+               page_cnt = qed_chain_get_page_cnt(&dev->cnq_array[i].pbl);
+               cur_pbl->num_pbl_pages = page_cnt;
+
+               p_phys_table = qed_chain_get_pbl_phys(&dev->cnq_array[i].pbl);
+               cur_pbl->pbl_ptr = (u64)p_phys_table;
+       }
+
+       events.affiliated_event = qedr_affiliated_event;
+       events.unaffiliated_event = qedr_unaffiliated_event;
+       events.context = dev;
+
+       in_params->events = &events;
+       in_params->cq_mode = QED_RDMA_CQ_MODE_32_BITS;
+       in_params->max_mtu = dev->ndev->mtu;
+       ether_addr_copy(&in_params->mac_addr[0], dev->ndev->dev_addr);
+
+       rc = dev->ops->rdma_init(dev->cdev, in_params);
+       if (rc)
+               goto out;
+
+       rc = dev->ops->rdma_add_user(dev->rdma_ctx, &out_params);
+       if (rc)
+               goto out;
+
+       dev->db_addr = (void *)(uintptr_t)out_params.dpi_addr;
+       dev->db_phys_addr = out_params.dpi_phys_addr;
+       dev->db_size = out_params.dpi_size;
+       dev->dpi = out_params.dpi;
+
+       rc = qedr_set_device_attr(dev);
+out:
+       kfree(in_params);
+       if (rc)
+               DP_ERR(dev, "Init HW Failed rc = %d\n", rc);
+
+       return rc;
+}
+
+void qedr_stop_hw(struct qedr_dev *dev)
+{
+       dev->ops->rdma_remove_user(dev->rdma_ctx, dev->dpi);
+       dev->ops->rdma_stop(dev->rdma_ctx);
+}
+
+static struct qedr_dev *qedr_add(struct qed_dev *cdev, struct pci_dev *pdev,
+                                struct net_device *ndev)
+{
+       struct qed_dev_rdma_info dev_info;
+       struct qedr_dev *dev;
+       int rc = 0, i;
+
+       dev = (struct qedr_dev *)ib_alloc_device(sizeof(*dev));
+       if (!dev) {
+               pr_err("Unable to allocate ib device\n");
+               return NULL;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "qedr add device called\n");
+
+       dev->pdev = pdev;
+       dev->ndev = ndev;
+       dev->cdev = cdev;
+
+       qed_ops = qed_get_rdma_ops();
+       if (!qed_ops) {
+               DP_ERR(dev, "Failed to get qed roce operations\n");
+               goto init_err;
+       }
+
+       dev->ops = qed_ops;
+       rc = qed_ops->fill_dev_info(cdev, &dev_info);
+       if (rc)
+               goto init_err;
+
+       dev->num_hwfns = dev_info.common.num_hwfns;
+       dev->rdma_ctx = dev->ops->rdma_get_rdma_ctx(cdev);
+
+       dev->num_cnq = dev->ops->rdma_get_min_cnq_msix(cdev);
+       if (!dev->num_cnq) {
+               DP_ERR(dev, "not enough CNQ resources.\n");
+               goto init_err;
+       }
+
+       dev->wq_multiplier = QEDR_WQ_MULTIPLIER_DFT;
+
+       qedr_pci_set_atomic(dev, pdev);
+
+       rc = qedr_alloc_resources(dev);
+       if (rc)
+               goto init_err;
+
+       rc = qedr_init_hw(dev);
+       if (rc)
+               goto alloc_err;
+
+       rc = qedr_setup_irqs(dev);
+       if (rc)
+               goto irq_err;
+
+       rc = qedr_register_device(dev);
+       if (rc) {
+               DP_ERR(dev, "Unable to allocate register device\n");
+               goto reg_err;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(qedr_attributes); i++)
+               if (device_create_file(&dev->ibdev.dev, qedr_attributes[i]))
+                       goto sysfs_err;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "qedr driver loaded successfully\n");
+       return dev;
+
+sysfs_err:
+       ib_unregister_device(&dev->ibdev);
+reg_err:
+       qedr_sync_free_irqs(dev);
+irq_err:
+       qedr_stop_hw(dev);
+alloc_err:
+       qedr_free_resources(dev);
+init_err:
+       ib_dealloc_device(&dev->ibdev);
+       DP_ERR(dev, "qedr driver load failed rc=%d\n", rc);
+
+       return NULL;
+}
+
+static void qedr_remove(struct qedr_dev *dev)
+{
+       /* First unregister with stack to stop all the active traffic
+        * of the registered clients.
+        */
+       qedr_remove_sysfiles(dev);
+       ib_unregister_device(&dev->ibdev);
+
+       qedr_stop_hw(dev);
+       qedr_sync_free_irqs(dev);
+       qedr_free_resources(dev);
+       ib_dealloc_device(&dev->ibdev);
+}
+
+static int qedr_close(struct qedr_dev *dev)
+{
+       qedr_ib_dispatch_event(dev, 1, IB_EVENT_PORT_ERR);
+
+       return 0;
+}
+
+static void qedr_shutdown(struct qedr_dev *dev)
+{
+       qedr_close(dev);
+       qedr_remove(dev);
+}
+
+static void qedr_mac_address_change(struct qedr_dev *dev)
+{
+       union ib_gid *sgid = &dev->sgid_tbl[0];
+       u8 guid[8], mac_addr[6];
+       int rc;
+
+       /* Update SGID */
+       ether_addr_copy(&mac_addr[0], dev->ndev->dev_addr);
+       guid[0] = mac_addr[0] ^ 2;
+       guid[1] = mac_addr[1];
+       guid[2] = mac_addr[2];
+       guid[3] = 0xff;
+       guid[4] = 0xfe;
+       guid[5] = mac_addr[3];
+       guid[6] = mac_addr[4];
+       guid[7] = mac_addr[5];
+       sgid->global.subnet_prefix = cpu_to_be64(0xfe80000000000000LL);
+       memcpy(&sgid->raw[8], guid, sizeof(guid));
+
+       /* Update LL2 */
+       rc = dev->ops->roce_ll2_set_mac_filter(dev->cdev,
+                                              dev->gsi_ll2_mac_address,
+                                              dev->ndev->dev_addr);
+
+       ether_addr_copy(dev->gsi_ll2_mac_address, dev->ndev->dev_addr);
+
+       qedr_ib_dispatch_event(dev, 1, IB_EVENT_GID_CHANGE);
+
+       if (rc)
+               DP_ERR(dev, "Error updating mac filter\n");
+}
+
+/* event handling via NIC driver ensures that all the NIC specific
+ * initialization done before RoCE driver notifies
+ * event to stack.
+ */
+static void qedr_notify(struct qedr_dev *dev, enum qede_roce_event event)
+{
+       switch (event) {
+       case QEDE_UP:
+               qedr_ib_dispatch_event(dev, 1, IB_EVENT_PORT_ACTIVE);
+               break;
+       case QEDE_DOWN:
+               qedr_close(dev);
+               break;
+       case QEDE_CLOSE:
+               qedr_shutdown(dev);
+               break;
+       case QEDE_CHANGE_ADDR:
+               qedr_mac_address_change(dev);
+               break;
+       default:
+               pr_err("Event not supported\n");
+       }
+}
+
+static struct qedr_driver qedr_drv = {
+       .name = "qedr_driver",
+       .add = qedr_add,
+       .remove = qedr_remove,
+       .notify = qedr_notify,
+};
+
+static int __init qedr_init_module(void)
+{
+       return qede_roce_register_driver(&qedr_drv);
+}
+
+static void __exit qedr_exit_module(void)
+{
+       qede_roce_unregister_driver(&qedr_drv);
+}
+
+module_init(qedr_init_module);
+module_exit(qedr_exit_module);
diff --git a/drivers/infiniband/hw/qedr/qedr.h b/drivers/infiniband/hw/qedr/qedr.h
new file mode 100644 (file)
index 0000000..620badd
--- /dev/null
@@ -0,0 +1,495 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed 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, or the
+ * OpenIB.org BSD license below:
+ *
+ *     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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __QEDR_H__
+#define __QEDR_H__
+
+#include <linux/pci.h>
+#include <rdma/ib_addr.h>
+#include <linux/qed/qed_if.h>
+#include <linux/qed/qed_chain.h>
+#include <linux/qed/qed_roce_if.h>
+#include <linux/qed/qede_roce.h>
+#include "qedr_hsi.h"
+
+#define QEDR_MODULE_VERSION    "8.10.10.0"
+#define QEDR_NODE_DESC "QLogic 579xx RoCE HCA"
+#define DP_NAME(dev) ((dev)->ibdev.name)
+
+#define DP_DEBUG(dev, module, fmt, ...)                                        \
+       pr_debug("(%s) " module ": " fmt,                               \
+                DP_NAME(dev) ? DP_NAME(dev) : "", ## __VA_ARGS__)
+
+#define QEDR_MSG_INIT "INIT"
+#define QEDR_MSG_MISC "MISC"
+#define QEDR_MSG_CQ   "  CQ"
+#define QEDR_MSG_MR   "  MR"
+#define QEDR_MSG_RQ   "  RQ"
+#define QEDR_MSG_SQ   "  SQ"
+#define QEDR_MSG_QP   "  QP"
+#define QEDR_MSG_GSI  " GSI"
+
+#define QEDR_CQ_MAGIC_NUMBER   (0x11223344)
+
+struct qedr_dev;
+
+struct qedr_cnq {
+       struct qedr_dev         *dev;
+       struct qed_chain        pbl;
+       struct qed_sb_info      *sb;
+       char                    name[32];
+       u64                     n_comp;
+       __le16                  *hw_cons_ptr;
+       u8                      index;
+};
+
+#define QEDR_MAX_SGID 128
+
+struct qedr_device_attr {
+       u32     vendor_id;
+       u32     vendor_part_id;
+       u32     hw_ver;
+       u64     fw_ver;
+       u64     node_guid;
+       u64     sys_image_guid;
+       u8      max_cnq;
+       u8      max_sge;
+       u16     max_inline;
+       u32     max_sqe;
+       u32     max_rqe;
+       u8      max_qp_resp_rd_atomic_resc;
+       u8      max_qp_req_rd_atomic_resc;
+       u64     max_dev_resp_rd_atomic_resc;
+       u32     max_cq;
+       u32     max_qp;
+       u32     max_mr;
+       u64     max_mr_size;
+       u32     max_cqe;
+       u32     max_mw;
+       u32     max_fmr;
+       u32     max_mr_mw_fmr_pbl;
+       u64     max_mr_mw_fmr_size;
+       u32     max_pd;
+       u32     max_ah;
+       u8      max_pkey;
+       u32     max_srq;
+       u32     max_srq_wr;
+       u8      max_srq_sge;
+       u8      max_stats_queues;
+       u32     dev_caps;
+
+       u64     page_size_caps;
+       u8      dev_ack_delay;
+       u32     reserved_lkey;
+       u32     bad_pkey_counter;
+       struct qed_rdma_events events;
+};
+
+struct qedr_dev {
+       struct ib_device        ibdev;
+       struct qed_dev          *cdev;
+       struct pci_dev          *pdev;
+       struct net_device       *ndev;
+
+       enum ib_atomic_cap      atomic_cap;
+
+       void *rdma_ctx;
+       struct qedr_device_attr attr;
+
+       const struct qed_rdma_ops *ops;
+       struct qed_int_info     int_info;
+
+       struct qed_sb_info      *sb_array;
+       struct qedr_cnq         *cnq_array;
+       int                     num_cnq;
+       int                     sb_start;
+
+       void __iomem            *db_addr;
+       u64                     db_phys_addr;
+       u32                     db_size;
+       u16                     dpi;
+
+       union ib_gid *sgid_tbl;
+
+       /* Lock for sgid table */
+       spinlock_t sgid_lock;
+
+       u64                     guid;
+
+       u32                     dp_module;
+       u8                      dp_level;
+       u8                      num_hwfns;
+       uint                    wq_multiplier;
+       u8                      gsi_ll2_mac_address[ETH_ALEN];
+       int                     gsi_qp_created;
+       struct qedr_cq          *gsi_sqcq;
+       struct qedr_cq          *gsi_rqcq;
+       struct qedr_qp          *gsi_qp;
+};
+
+#define QEDR_MAX_SQ_PBL                        (0x8000)
+#define QEDR_MAX_SQ_PBL_ENTRIES                (0x10000 / sizeof(void *))
+#define QEDR_SQE_ELEMENT_SIZE          (sizeof(struct rdma_sq_sge))
+#define QEDR_MAX_SQE_ELEMENTS_PER_SQE  (ROCE_REQ_MAX_SINGLE_SQ_WQE_SIZE / \
+                                        QEDR_SQE_ELEMENT_SIZE)
+#define QEDR_MAX_SQE_ELEMENTS_PER_PAGE ((RDMA_RING_PAGE_SIZE) / \
+                                        QEDR_SQE_ELEMENT_SIZE)
+#define QEDR_MAX_SQE                   ((QEDR_MAX_SQ_PBL_ENTRIES) *\
+                                        (RDMA_RING_PAGE_SIZE) / \
+                                        (QEDR_SQE_ELEMENT_SIZE) /\
+                                        (QEDR_MAX_SQE_ELEMENTS_PER_SQE))
+/* RQ */
+#define QEDR_MAX_RQ_PBL                        (0x2000)
+#define QEDR_MAX_RQ_PBL_ENTRIES                (0x10000 / sizeof(void *))
+#define QEDR_RQE_ELEMENT_SIZE          (sizeof(struct rdma_rq_sge))
+#define QEDR_MAX_RQE_ELEMENTS_PER_RQE  (RDMA_MAX_SGE_PER_RQ_WQE)
+#define QEDR_MAX_RQE_ELEMENTS_PER_PAGE ((RDMA_RING_PAGE_SIZE) / \
+                                        QEDR_RQE_ELEMENT_SIZE)
+#define QEDR_MAX_RQE                   ((QEDR_MAX_RQ_PBL_ENTRIES) *\
+                                        (RDMA_RING_PAGE_SIZE) / \
+                                        (QEDR_RQE_ELEMENT_SIZE) /\
+                                        (QEDR_MAX_RQE_ELEMENTS_PER_RQE))
+
+#define QEDR_CQE_SIZE  (sizeof(union rdma_cqe))
+#define QEDR_MAX_CQE_PBL_SIZE (512 * 1024)
+#define QEDR_MAX_CQE_PBL_ENTRIES (((QEDR_MAX_CQE_PBL_SIZE) / \
+                                 sizeof(u64)) - 1)
+#define QEDR_MAX_CQES ((u32)((QEDR_MAX_CQE_PBL_ENTRIES) * \
+                            (QED_CHAIN_PAGE_SIZE) / QEDR_CQE_SIZE))
+
+#define QEDR_ROCE_MAX_CNQ_SIZE         (0x4000)
+
+#define QEDR_MAX_PORT                  (1)
+
+#define QEDR_UVERBS(CMD_NAME) (1ull << IB_USER_VERBS_CMD_##CMD_NAME)
+
+#define QEDR_ROCE_PKEY_MAX 1
+#define QEDR_ROCE_PKEY_TABLE_LEN 1
+#define QEDR_ROCE_PKEY_DEFAULT 0xffff
+
+struct qedr_pbl {
+       struct list_head list_entry;
+       void *va;
+       dma_addr_t pa;
+};
+
+struct qedr_ucontext {
+       struct ib_ucontext ibucontext;
+       struct qedr_dev *dev;
+       struct qedr_pd *pd;
+       u64 dpi_addr;
+       u64 dpi_phys_addr;
+       u32 dpi_size;
+       u16 dpi;
+
+       struct list_head mm_head;
+
+       /* Lock to protect mm list */
+       struct mutex mm_list_lock;
+};
+
+union db_prod64 {
+       struct rdma_pwm_val32_data data;
+       u64 raw;
+};
+
+enum qedr_cq_type {
+       QEDR_CQ_TYPE_GSI,
+       QEDR_CQ_TYPE_KERNEL,
+       QEDR_CQ_TYPE_USER,
+};
+
+struct qedr_pbl_info {
+       u32 num_pbls;
+       u32 num_pbes;
+       u32 pbl_size;
+       u32 pbe_size;
+       bool two_layered;
+};
+
+struct qedr_userq {
+       struct ib_umem *umem;
+       struct qedr_pbl_info pbl_info;
+       struct qedr_pbl *pbl_tbl;
+       u64 buf_addr;
+       size_t buf_len;
+};
+
+struct qedr_cq {
+       struct ib_cq ibcq;
+
+       enum qedr_cq_type cq_type;
+       u32 sig;
+
+       u16 icid;
+
+       /* Lock to protect completion handler */
+       spinlock_t comp_handler_lock;
+
+       /* Lock to protect multiplem CQ's */
+       spinlock_t cq_lock;
+       u8 arm_flags;
+       struct qed_chain pbl;
+
+       void __iomem *db_addr;
+       union db_prod64 db;
+
+       u8 pbl_toggle;
+       union rdma_cqe *latest_cqe;
+       union rdma_cqe *toggle_cqe;
+
+       u32 cq_cons;
+
+       struct qedr_userq q;
+};
+
+struct qedr_pd {
+       struct ib_pd ibpd;
+       u32 pd_id;
+       struct qedr_ucontext *uctx;
+};
+
+struct qedr_mm {
+       struct {
+               u64 phy_addr;
+               unsigned long len;
+       } key;
+       struct list_head entry;
+};
+
+union db_prod32 {
+       struct rdma_pwm_val16_data data;
+       u32 raw;
+};
+
+struct qedr_qp_hwq_info {
+       /* WQE Elements */
+       struct qed_chain pbl;
+       u64 p_phys_addr_tbl;
+       u32 max_sges;
+
+       /* WQE */
+       u16 prod;
+       u16 cons;
+       u16 wqe_cons;
+       u16 gsi_cons;
+       u16 max_wr;
+
+       /* DB */
+       void __iomem *db;
+       union db_prod32 db_data;
+};
+
+#define QEDR_INC_SW_IDX(p_info, index)                                 \
+       do {                                                            \
+               p_info->index = (p_info->index + 1) &                   \
+                               qed_chain_get_capacity(p_info->pbl)     \
+       } while (0)
+
+enum qedr_qp_err_bitmap {
+       QEDR_QP_ERR_SQ_FULL = 1,
+       QEDR_QP_ERR_RQ_FULL = 2,
+       QEDR_QP_ERR_BAD_SR = 4,
+       QEDR_QP_ERR_BAD_RR = 8,
+       QEDR_QP_ERR_SQ_PBL_FULL = 16,
+       QEDR_QP_ERR_RQ_PBL_FULL = 32,
+};
+
+struct qedr_qp {
+       struct ib_qp ibqp;      /* must be first */
+       struct qedr_dev *dev;
+
+       struct qedr_qp_hwq_info sq;
+       struct qedr_qp_hwq_info rq;
+
+       u32 max_inline_data;
+
+       /* Lock for QP's */
+       spinlock_t q_lock;
+       struct qedr_cq *sq_cq;
+       struct qedr_cq *rq_cq;
+       struct qedr_srq *srq;
+       enum qed_roce_qp_state state;
+       u32 id;
+       struct qedr_pd *pd;
+       enum ib_qp_type qp_type;
+       struct qed_rdma_qp *qed_qp;
+       u32 qp_id;
+       u16 icid;
+       u16 mtu;
+       int sgid_idx;
+       u32 rq_psn;
+       u32 sq_psn;
+       u32 qkey;
+       u32 dest_qp_num;
+
+       /* Relevant to qps created from kernel space only (ULPs) */
+       u8 prev_wqe_size;
+       u16 wqe_cons;
+       u32 err_bitmap;
+       bool signaled;
+
+       /* SQ shadow */
+       struct {
+               u64 wr_id;
+               enum ib_wc_opcode opcode;
+               u32 bytes_len;
+               u8 wqe_size;
+               bool signaled;
+               dma_addr_t icrc_mapping;
+               u32 *icrc;
+               struct qedr_mr *mr;
+       } *wqe_wr_id;
+
+       /* RQ shadow */
+       struct {
+               u64 wr_id;
+               struct ib_sge sg_list[RDMA_MAX_SGE_PER_RQ_WQE];
+               u8 wqe_size;
+
+               u8 smac[ETH_ALEN];
+               u16 vlan_id;
+               int rc;
+       } *rqe_wr_id;
+
+       /* Relevant to qps created from user space only (applications) */
+       struct qedr_userq usq;
+       struct qedr_userq urq;
+};
+
+struct qedr_ah {
+       struct ib_ah ibah;
+       struct ib_ah_attr attr;
+};
+
+enum qedr_mr_type {
+       QEDR_MR_USER,
+       QEDR_MR_KERNEL,
+       QEDR_MR_DMA,
+       QEDR_MR_FRMR,
+};
+
+struct mr_info {
+       struct qedr_pbl *pbl_table;
+       struct qedr_pbl_info pbl_info;
+       struct list_head free_pbl_list;
+       struct list_head inuse_pbl_list;
+       u32 completed;
+       u32 completed_handled;
+};
+
+struct qedr_mr {
+       struct ib_mr ibmr;
+       struct ib_umem *umem;
+
+       struct qed_rdma_register_tid_in_params hw_mr;
+       enum qedr_mr_type type;
+
+       struct qedr_dev *dev;
+       struct mr_info info;
+
+       u64 *pages;
+       u32 npages;
+};
+
+#define SET_FIELD2(value, name, flag) ((value) |= ((flag) << (name ## _SHIFT)))
+
+#define QEDR_RESP_IMM  (RDMA_CQE_RESPONDER_IMM_FLG_MASK << \
+                        RDMA_CQE_RESPONDER_IMM_FLG_SHIFT)
+#define QEDR_RESP_RDMA (RDMA_CQE_RESPONDER_RDMA_FLG_MASK << \
+                        RDMA_CQE_RESPONDER_RDMA_FLG_SHIFT)
+#define QEDR_RESP_RDMA_IMM (QEDR_RESP_IMM | QEDR_RESP_RDMA)
+
+static inline void qedr_inc_sw_cons(struct qedr_qp_hwq_info *info)
+{
+       info->cons = (info->cons + 1) % info->max_wr;
+       info->wqe_cons++;
+}
+
+static inline void qedr_inc_sw_prod(struct qedr_qp_hwq_info *info)
+{
+       info->prod = (info->prod + 1) % info->max_wr;
+}
+
+static inline int qedr_get_dmac(struct qedr_dev *dev,
+                               struct ib_ah_attr *ah_attr, u8 *mac_addr)
+{
+       union ib_gid zero_sgid = { { 0 } };
+       struct in6_addr in6;
+
+       if (!memcmp(&ah_attr->grh.dgid, &zero_sgid, sizeof(union ib_gid))) {
+               DP_ERR(dev, "Local port GID not supported\n");
+               eth_zero_addr(mac_addr);
+               return -EINVAL;
+       }
+
+       memcpy(&in6, ah_attr->grh.dgid.raw, sizeof(in6));
+       ether_addr_copy(mac_addr, ah_attr->dmac);
+
+       return 0;
+}
+
+static inline
+struct qedr_ucontext *get_qedr_ucontext(struct ib_ucontext *ibucontext)
+{
+       return container_of(ibucontext, struct qedr_ucontext, ibucontext);
+}
+
+static inline struct qedr_dev *get_qedr_dev(struct ib_device *ibdev)
+{
+       return container_of(ibdev, struct qedr_dev, ibdev);
+}
+
+static inline struct qedr_pd *get_qedr_pd(struct ib_pd *ibpd)
+{
+       return container_of(ibpd, struct qedr_pd, ibpd);
+}
+
+static inline struct qedr_cq *get_qedr_cq(struct ib_cq *ibcq)
+{
+       return container_of(ibcq, struct qedr_cq, ibcq);
+}
+
+static inline struct qedr_qp *get_qedr_qp(struct ib_qp *ibqp)
+{
+       return container_of(ibqp, struct qedr_qp, ibqp);
+}
+
+static inline struct qedr_ah *get_qedr_ah(struct ib_ah *ibah)
+{
+       return container_of(ibah, struct qedr_ah, ibah);
+}
+
+static inline struct qedr_mr *get_qedr_mr(struct ib_mr *ibmr)
+{
+       return container_of(ibmr, struct qedr_mr, ibmr);
+}
+#endif
diff --git a/drivers/infiniband/hw/qedr/qedr_cm.c b/drivers/infiniband/hw/qedr/qedr_cm.c
new file mode 100644 (file)
index 0000000..63890eb
--- /dev/null
@@ -0,0 +1,622 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed 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, or the
+ * OpenIB.org BSD license below:
+ *
+ *     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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/dma-mapping.h>
+#include <linux/crc32.h>
+#include <linux/iommu.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
+
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_user_verbs.h>
+#include <rdma/iw_cm.h>
+#include <rdma/ib_umem.h>
+#include <rdma/ib_addr.h>
+#include <rdma/ib_cache.h>
+
+#include "qedr_hsi.h"
+#include <linux/qed/qed_if.h>
+#include <linux/qed/qed_roce_if.h>
+#include "qedr.h"
+#include "qedr_hsi.h"
+#include "verbs.h"
+#include <rdma/qedr-abi.h>
+#include "qedr_hsi.h"
+#include "qedr_cm.h"
+
+void qedr_inc_sw_gsi_cons(struct qedr_qp_hwq_info *info)
+{
+       info->gsi_cons = (info->gsi_cons + 1) % info->max_wr;
+}
+
+void qedr_store_gsi_qp_cq(struct qedr_dev *dev, struct qedr_qp *qp,
+                         struct ib_qp_init_attr *attrs)
+{
+       dev->gsi_qp_created = 1;
+       dev->gsi_sqcq = get_qedr_cq(attrs->send_cq);
+       dev->gsi_rqcq = get_qedr_cq(attrs->recv_cq);
+       dev->gsi_qp = qp;
+}
+
+void qedr_ll2_tx_cb(void *_qdev, struct qed_roce_ll2_packet *pkt)
+{
+       struct qedr_dev *dev = (struct qedr_dev *)_qdev;
+       struct qedr_cq *cq = dev->gsi_sqcq;
+       struct qedr_qp *qp = dev->gsi_qp;
+       unsigned long flags;
+
+       DP_DEBUG(dev, QEDR_MSG_GSI,
+                "LL2 TX CB: gsi_sqcq=%p, gsi_rqcq=%p, gsi_cons=%d, ibcq_comp=%s\n",
+                dev->gsi_sqcq, dev->gsi_rqcq, qp->sq.gsi_cons,
+                cq->ibcq.comp_handler ? "Yes" : "No");
+
+       dma_free_coherent(&dev->pdev->dev, pkt->header.len, pkt->header.vaddr,
+                         pkt->header.baddr);
+       kfree(pkt);
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+       qedr_inc_sw_gsi_cons(&qp->sq);
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       if (cq->ibcq.comp_handler) {
+               spin_lock_irqsave(&cq->comp_handler_lock, flags);
+               (*cq->ibcq.comp_handler) (&cq->ibcq, cq->ibcq.cq_context);
+               spin_unlock_irqrestore(&cq->comp_handler_lock, flags);
+       }
+}
+
+void qedr_ll2_rx_cb(void *_dev, struct qed_roce_ll2_packet *pkt,
+                   struct qed_roce_ll2_rx_params *params)
+{
+       struct qedr_dev *dev = (struct qedr_dev *)_dev;
+       struct qedr_cq *cq = dev->gsi_rqcq;
+       struct qedr_qp *qp = dev->gsi_qp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+
+       qp->rqe_wr_id[qp->rq.gsi_cons].rc = params->rc;
+       qp->rqe_wr_id[qp->rq.gsi_cons].vlan_id = params->vlan_id;
+       qp->rqe_wr_id[qp->rq.gsi_cons].sg_list[0].length = pkt->payload[0].len;
+       ether_addr_copy(qp->rqe_wr_id[qp->rq.gsi_cons].smac, params->smac);
+
+       qedr_inc_sw_gsi_cons(&qp->rq);
+
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       if (cq->ibcq.comp_handler) {
+               spin_lock_irqsave(&cq->comp_handler_lock, flags);
+               (*cq->ibcq.comp_handler) (&cq->ibcq, cq->ibcq.cq_context);
+               spin_unlock_irqrestore(&cq->comp_handler_lock, flags);
+       }
+}
+
+static void qedr_destroy_gsi_cq(struct qedr_dev *dev,
+                               struct ib_qp_init_attr *attrs)
+{
+       struct qed_rdma_destroy_cq_in_params iparams;
+       struct qed_rdma_destroy_cq_out_params oparams;
+       struct qedr_cq *cq;
+
+       cq = get_qedr_cq(attrs->send_cq);
+       iparams.icid = cq->icid;
+       dev->ops->rdma_destroy_cq(dev->rdma_ctx, &iparams, &oparams);
+       dev->ops->common->chain_free(dev->cdev, &cq->pbl);
+
+       cq = get_qedr_cq(attrs->recv_cq);
+       /* if a dedicated recv_cq was used, delete it too */
+       if (iparams.icid != cq->icid) {
+               iparams.icid = cq->icid;
+               dev->ops->rdma_destroy_cq(dev->rdma_ctx, &iparams, &oparams);
+               dev->ops->common->chain_free(dev->cdev, &cq->pbl);
+       }
+}
+
+static inline int qedr_check_gsi_qp_attrs(struct qedr_dev *dev,
+                                         struct ib_qp_init_attr *attrs)
+{
+       if (attrs->cap.max_recv_sge > QEDR_GSI_MAX_RECV_SGE) {
+               DP_ERR(dev,
+                      " create gsi qp: failed. max_recv_sge is larger the max %d>%d\n",
+                      attrs->cap.max_recv_sge, QEDR_GSI_MAX_RECV_SGE);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_recv_wr > QEDR_GSI_MAX_RECV_WR) {
+               DP_ERR(dev,
+                      " create gsi qp: failed. max_recv_wr is too large %d>%d\n",
+                      attrs->cap.max_recv_wr, QEDR_GSI_MAX_RECV_WR);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_send_wr > QEDR_GSI_MAX_SEND_WR) {
+               DP_ERR(dev,
+                      " create gsi qp: failed. max_send_wr is too large %d>%d\n",
+                      attrs->cap.max_send_wr, QEDR_GSI_MAX_SEND_WR);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct ib_qp *qedr_create_gsi_qp(struct qedr_dev *dev,
+                                struct ib_qp_init_attr *attrs,
+                                struct qedr_qp *qp)
+{
+       struct qed_roce_ll2_params ll2_params;
+       int rc;
+
+       rc = qedr_check_gsi_qp_attrs(dev, attrs);
+       if (rc)
+               return ERR_PTR(rc);
+
+       /* configure and start LL2 */
+       memset(&ll2_params, 0, sizeof(ll2_params));
+       ll2_params.max_tx_buffers = attrs->cap.max_send_wr;
+       ll2_params.max_rx_buffers = attrs->cap.max_recv_wr;
+       ll2_params.cbs.tx_cb = qedr_ll2_tx_cb;
+       ll2_params.cbs.rx_cb = qedr_ll2_rx_cb;
+       ll2_params.cb_cookie = (void *)dev;
+       ll2_params.mtu = dev->ndev->mtu;
+       ether_addr_copy(ll2_params.mac_address, dev->ndev->dev_addr);
+       rc = dev->ops->roce_ll2_start(dev->cdev, &ll2_params);
+       if (rc) {
+               DP_ERR(dev, "create gsi qp: failed on ll2 start. rc=%d\n", rc);
+               return ERR_PTR(rc);
+       }
+
+       /* create QP */
+       qp->ibqp.qp_num = 1;
+       qp->rq.max_wr = attrs->cap.max_recv_wr;
+       qp->sq.max_wr = attrs->cap.max_send_wr;
+
+       qp->rqe_wr_id = kcalloc(qp->rq.max_wr, sizeof(*qp->rqe_wr_id),
+                               GFP_KERNEL);
+       if (!qp->rqe_wr_id)
+               goto err;
+       qp->wqe_wr_id = kcalloc(qp->sq.max_wr, sizeof(*qp->wqe_wr_id),
+                               GFP_KERNEL);
+       if (!qp->wqe_wr_id)
+               goto err;
+
+       qedr_store_gsi_qp_cq(dev, qp, attrs);
+       ether_addr_copy(dev->gsi_ll2_mac_address, dev->ndev->dev_addr);
+
+       /* the GSI CQ is handled by the driver so remove it from the FW */
+       qedr_destroy_gsi_cq(dev, attrs);
+       dev->gsi_rqcq->cq_type = QEDR_CQ_TYPE_GSI;
+       dev->gsi_rqcq->cq_type = QEDR_CQ_TYPE_GSI;
+
+       DP_DEBUG(dev, QEDR_MSG_GSI, "created GSI QP %p\n", qp);
+
+       return &qp->ibqp;
+
+err:
+       kfree(qp->rqe_wr_id);
+
+       rc = dev->ops->roce_ll2_stop(dev->cdev);
+       if (rc)
+               DP_ERR(dev, "create gsi qp: failed destroy on create\n");
+
+       return ERR_PTR(-ENOMEM);
+}
+
+int qedr_destroy_gsi_qp(struct qedr_dev *dev)
+{
+       int rc;
+
+       rc = dev->ops->roce_ll2_stop(dev->cdev);
+       if (rc)
+               DP_ERR(dev, "destroy gsi qp: failed (rc=%d)\n", rc);
+       else
+               DP_DEBUG(dev, QEDR_MSG_GSI, "destroy gsi qp: success\n");
+
+       return rc;
+}
+
+#define QEDR_MAX_UD_HEADER_SIZE        (100)
+#define QEDR_GSI_QPN           (1)
+static inline int qedr_gsi_build_header(struct qedr_dev *dev,
+                                       struct qedr_qp *qp,
+                                       struct ib_send_wr *swr,
+                                       struct ib_ud_header *udh,
+                                       int *roce_mode)
+{
+       bool has_vlan = false, has_grh_ipv6 = true;
+       struct ib_ah_attr *ah_attr = &get_qedr_ah(ud_wr(swr)->ah)->attr;
+       struct ib_global_route *grh = &ah_attr->grh;
+       union ib_gid sgid;
+       int send_size = 0;
+       u16 vlan_id = 0;
+       u16 ether_type;
+       struct ib_gid_attr sgid_attr;
+       int rc;
+       int ip_ver = 0;
+
+       bool has_udp = false;
+       int i;
+
+       send_size = 0;
+       for (i = 0; i < swr->num_sge; ++i)
+               send_size += swr->sg_list[i].length;
+
+       rc = ib_get_cached_gid(qp->ibqp.device, ah_attr->port_num,
+                              grh->sgid_index, &sgid, &sgid_attr);
+       if (rc) {
+               DP_ERR(dev,
+                      "gsi post send: failed to get cached GID (port=%d, ix=%d)\n",
+                      ah_attr->port_num, grh->sgid_index);
+               return rc;
+       }
+
+       vlan_id = rdma_vlan_dev_vlan_id(sgid_attr.ndev);
+       if (vlan_id < VLAN_CFI_MASK)
+               has_vlan = true;
+       if (sgid_attr.ndev)
+               dev_put(sgid_attr.ndev);
+
+       if (!memcmp(&sgid, &zgid, sizeof(sgid))) {
+               DP_ERR(dev, "gsi post send: GID not found GID index %d\n",
+                      ah_attr->grh.sgid_index);
+               return -ENOENT;
+       }
+
+       has_udp = (sgid_attr.gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP);
+       if (!has_udp) {
+               /* RoCE v1 */
+               ether_type = ETH_P_ROCE;
+               *roce_mode = ROCE_V1;
+       } else if (ipv6_addr_v4mapped((struct in6_addr *)&sgid)) {
+               /* RoCE v2 IPv4 */
+               ip_ver = 4;
+               ether_type = ETH_P_IP;
+               has_grh_ipv6 = false;
+               *roce_mode = ROCE_V2_IPV4;
+       } else {
+               /* RoCE v2 IPv6 */
+               ip_ver = 6;
+               ether_type = ETH_P_IPV6;
+               *roce_mode = ROCE_V2_IPV6;
+       }
+
+       rc = ib_ud_header_init(send_size, false, true, has_vlan,
+                              has_grh_ipv6, ip_ver, has_udp, 0, udh);
+       if (rc) {
+               DP_ERR(dev, "gsi post send: failed to init header\n");
+               return rc;
+       }
+
+       /* ENET + VLAN headers */
+       ether_addr_copy(udh->eth.dmac_h, ah_attr->dmac);
+       ether_addr_copy(udh->eth.smac_h, dev->ndev->dev_addr);
+       if (has_vlan) {
+               udh->eth.type = htons(ETH_P_8021Q);
+               udh->vlan.tag = htons(vlan_id);
+               udh->vlan.type = htons(ether_type);
+       } else {
+               udh->eth.type = htons(ether_type);
+       }
+
+       /* BTH */
+       udh->bth.solicited_event = !!(swr->send_flags & IB_SEND_SOLICITED);
+       udh->bth.pkey = QEDR_ROCE_PKEY_DEFAULT;
+       udh->bth.destination_qpn = htonl(ud_wr(swr)->remote_qpn);
+       udh->bth.psn = htonl((qp->sq_psn++) & ((1 << 24) - 1));
+       udh->bth.opcode = IB_OPCODE_UD_SEND_ONLY;
+
+       /* DETH */
+       udh->deth.qkey = htonl(0x80010000);
+       udh->deth.source_qpn = htonl(QEDR_GSI_QPN);
+
+       if (has_grh_ipv6) {
+               /* GRH / IPv6 header */
+               udh->grh.traffic_class = grh->traffic_class;
+               udh->grh.flow_label = grh->flow_label;
+               udh->grh.hop_limit = grh->hop_limit;
+               udh->grh.destination_gid = grh->dgid;
+               memcpy(&udh->grh.source_gid.raw, &sgid.raw,
+                      sizeof(udh->grh.source_gid.raw));
+       } else {
+               /* IPv4 header */
+               u32 ipv4_addr;
+
+               udh->ip4.protocol = IPPROTO_UDP;
+               udh->ip4.tos = htonl(ah_attr->grh.flow_label);
+               udh->ip4.frag_off = htons(IP_DF);
+               udh->ip4.ttl = ah_attr->grh.hop_limit;
+
+               ipv4_addr = qedr_get_ipv4_from_gid(sgid.raw);
+               udh->ip4.saddr = ipv4_addr;
+               ipv4_addr = qedr_get_ipv4_from_gid(ah_attr->grh.dgid.raw);
+               udh->ip4.daddr = ipv4_addr;
+               /* note: checksum is calculated by the device */
+       }
+
+       /* UDP */
+       if (has_udp) {
+               udh->udp.sport = htons(QEDR_ROCE_V2_UDP_SPORT);
+               udh->udp.dport = htons(ROCE_V2_UDP_DPORT);
+               udh->udp.csum = 0;
+               /* UDP length is untouched hence is zero */
+       }
+       return 0;
+}
+
+static inline int qedr_gsi_build_packet(struct qedr_dev *dev,
+                                       struct qedr_qp *qp,
+                                       struct ib_send_wr *swr,
+                                       struct qed_roce_ll2_packet **p_packet)
+{
+       u8 ud_header_buffer[QEDR_MAX_UD_HEADER_SIZE];
+       struct qed_roce_ll2_packet *packet;
+       struct pci_dev *pdev = dev->pdev;
+       int roce_mode, header_size;
+       struct ib_ud_header udh;
+       int i, rc;
+
+       *p_packet = NULL;
+
+       rc = qedr_gsi_build_header(dev, qp, swr, &udh, &roce_mode);
+       if (rc)
+               return rc;
+
+       header_size = ib_ud_header_pack(&udh, &ud_header_buffer);
+
+       packet = kzalloc(sizeof(*packet), GFP_ATOMIC);
+       if (!packet)
+               return -ENOMEM;
+
+       packet->header.vaddr = dma_alloc_coherent(&pdev->dev, header_size,
+                                                 &packet->header.baddr,
+                                                 GFP_ATOMIC);
+       if (!packet->header.vaddr) {
+               kfree(packet);
+               return -ENOMEM;
+       }
+
+       if (ether_addr_equal(udh.eth.smac_h, udh.eth.dmac_h))
+               packet->tx_dest = QED_ROCE_LL2_TX_DEST_NW;
+       else
+               packet->tx_dest = QED_ROCE_LL2_TX_DEST_LB;
+
+       packet->roce_mode = roce_mode;
+       memcpy(packet->header.vaddr, ud_header_buffer, header_size);
+       packet->header.len = header_size;
+       packet->n_seg = swr->num_sge;
+       for (i = 0; i < packet->n_seg; i++) {
+               packet->payload[i].baddr = swr->sg_list[i].addr;
+               packet->payload[i].len = swr->sg_list[i].length;
+       }
+
+       *p_packet = packet;
+
+       return 0;
+}
+
+int qedr_gsi_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                      struct ib_send_wr **bad_wr)
+{
+       struct qed_roce_ll2_packet *pkt = NULL;
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qed_roce_ll2_tx_params params;
+       struct qedr_dev *dev = qp->dev;
+       unsigned long flags;
+       int rc;
+
+       if (qp->state != QED_ROCE_QP_STATE_RTS) {
+               *bad_wr = wr;
+               DP_ERR(dev,
+                      "gsi post recv: failed to post rx buffer. state is %d and not QED_ROCE_QP_STATE_RTS\n",
+                      qp->state);
+               return -EINVAL;
+       }
+
+       if (wr->num_sge > RDMA_MAX_SGE_PER_SQ_WQE) {
+               DP_ERR(dev, "gsi post send: num_sge is too large (%d>%d)\n",
+                      wr->num_sge, RDMA_MAX_SGE_PER_SQ_WQE);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       if (wr->opcode != IB_WR_SEND) {
+               DP_ERR(dev,
+                      "gsi post send: failed due to unsupported opcode %d\n",
+                      wr->opcode);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       memset(&params, 0, sizeof(params));
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+
+       rc = qedr_gsi_build_packet(dev, qp, wr, &pkt);
+       if (rc) {
+               spin_unlock_irqrestore(&qp->q_lock, flags);
+               goto err;
+       }
+
+       rc = dev->ops->roce_ll2_tx(dev->cdev, pkt, &params);
+       if (!rc) {
+               qp->wqe_wr_id[qp->sq.prod].wr_id = wr->wr_id;
+               qedr_inc_sw_prod(&qp->sq);
+               DP_DEBUG(qp->dev, QEDR_MSG_GSI,
+                        "gsi post send: opcode=%d, in_irq=%ld, irqs_disabled=%d, wr_id=%llx\n",
+                        wr->opcode, in_irq(), irqs_disabled(), wr->wr_id);
+       } else {
+               if (rc == QED_ROCE_TX_HEAD_FAILURE) {
+                       /* TX failed while posting header - release resources */
+                       dma_free_coherent(&dev->pdev->dev, pkt->header.len,
+                                         pkt->header.vaddr, pkt->header.baddr);
+                       kfree(pkt);
+               } else if (rc == QED_ROCE_TX_FRAG_FAILURE) {
+                       /* NTD since TX failed while posting a fragment. We will
+                        * release the resources on TX callback
+                        */
+               }
+
+               DP_ERR(dev, "gsi post send: failed to transmit (rc=%d)\n", rc);
+               rc = -EAGAIN;
+               *bad_wr = wr;
+       }
+
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       if (wr->next) {
+               DP_ERR(dev,
+                      "gsi post send: failed second WR. Only one WR may be passed at a time\n");
+               *bad_wr = wr->next;
+               rc = -EINVAL;
+       }
+
+       return rc;
+
+err:
+       *bad_wr = wr;
+       return rc;
+}
+
+int qedr_gsi_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+                      struct ib_recv_wr **bad_wr)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibqp->device);
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qed_roce_ll2_buffer buf;
+       unsigned long flags;
+       int status = 0;
+       int rc;
+
+       if ((qp->state != QED_ROCE_QP_STATE_RTR) &&
+           (qp->state != QED_ROCE_QP_STATE_RTS)) {
+               *bad_wr = wr;
+               DP_ERR(dev,
+                      "gsi post recv: failed to post rx buffer. state is %d and not QED_ROCE_QP_STATE_RTR/S\n",
+                      qp->state);
+               return -EINVAL;
+       }
+
+       memset(&buf, 0, sizeof(buf));
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+
+       while (wr) {
+               if (wr->num_sge > QEDR_GSI_MAX_RECV_SGE) {
+                       DP_ERR(dev,
+                              "gsi post recv: failed to post rx buffer. too many sges %d>%d\n",
+                              wr->num_sge, QEDR_GSI_MAX_RECV_SGE);
+                       goto err;
+               }
+
+               buf.baddr = wr->sg_list[0].addr;
+               buf.len = wr->sg_list[0].length;
+
+               rc = dev->ops->roce_ll2_post_rx_buffer(dev->cdev, &buf, 0, 1);
+               if (rc) {
+                       DP_ERR(dev,
+                              "gsi post recv: failed to post rx buffer (rc=%d)\n",
+                              rc);
+                       goto err;
+               }
+
+               memset(&qp->rqe_wr_id[qp->rq.prod], 0,
+                      sizeof(qp->rqe_wr_id[qp->rq.prod]));
+               qp->rqe_wr_id[qp->rq.prod].sg_list[0] = wr->sg_list[0];
+               qp->rqe_wr_id[qp->rq.prod].wr_id = wr->wr_id;
+
+               qedr_inc_sw_prod(&qp->rq);
+
+               wr = wr->next;
+       }
+
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       return status;
+err:
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+       *bad_wr = wr;
+       return -ENOMEM;
+}
+
+int qedr_gsi_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibcq->device);
+       struct qedr_cq *cq = get_qedr_cq(ibcq);
+       struct qedr_qp *qp = dev->gsi_qp;
+       unsigned long flags;
+       int i = 0;
+
+       spin_lock_irqsave(&cq->cq_lock, flags);
+
+       while (i < num_entries && qp->rq.cons != qp->rq.gsi_cons) {
+               memset(&wc[i], 0, sizeof(*wc));
+
+               wc[i].qp = &qp->ibqp;
+               wc[i].wr_id = qp->rqe_wr_id[qp->rq.cons].wr_id;
+               wc[i].opcode = IB_WC_RECV;
+               wc[i].pkey_index = 0;
+               wc[i].status = (qp->rqe_wr_id[qp->rq.cons].rc) ?
+                   IB_WC_GENERAL_ERR : IB_WC_SUCCESS;
+               /* 0 - currently only one recv sg is supported */
+               wc[i].byte_len = qp->rqe_wr_id[qp->rq.cons].sg_list[0].length;
+               wc[i].wc_flags |= IB_WC_GRH | IB_WC_IP_CSUM_OK;
+               ether_addr_copy(wc[i].smac, qp->rqe_wr_id[qp->rq.cons].smac);
+               wc[i].wc_flags |= IB_WC_WITH_SMAC;
+               if (qp->rqe_wr_id[qp->rq.cons].vlan_id) {
+                       wc[i].wc_flags |= IB_WC_WITH_VLAN;
+                       wc[i].vlan_id = qp->rqe_wr_id[qp->rq.cons].vlan_id;
+               }
+
+               qedr_inc_sw_cons(&qp->rq);
+               i++;
+       }
+
+       while (i < num_entries && qp->sq.cons != qp->sq.gsi_cons) {
+               memset(&wc[i], 0, sizeof(*wc));
+
+               wc[i].qp = &qp->ibqp;
+               wc[i].wr_id = qp->wqe_wr_id[qp->sq.cons].wr_id;
+               wc[i].opcode = IB_WC_SEND;
+               wc[i].status = IB_WC_SUCCESS;
+
+               qedr_inc_sw_cons(&qp->sq);
+               i++;
+       }
+
+       spin_unlock_irqrestore(&cq->cq_lock, flags);
+
+       DP_DEBUG(dev, QEDR_MSG_GSI,
+                "gsi poll_cq: requested entries=%d, actual=%d, qp->rq.cons=%d, qp->rq.gsi_cons=%x, qp->sq.cons=%d, qp->sq.gsi_cons=%d, qp_num=%d\n",
+                num_entries, i, qp->rq.cons, qp->rq.gsi_cons, qp->sq.cons,
+                qp->sq.gsi_cons, qp->ibqp.qp_num);
+
+       return i;
+}
diff --git a/drivers/infiniband/hw/qedr/qedr_cm.h b/drivers/infiniband/hw/qedr/qedr_cm.h
new file mode 100644 (file)
index 0000000..9ba6e15
--- /dev/null
@@ -0,0 +1,61 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed 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, or the
+ * OpenIB.org BSD license below:
+ *
+ *     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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef LINUX_QEDR_CM_H_
+#define LINUX_QEDR_CM_H_
+
+#define QEDR_GSI_MAX_RECV_WR   (4096)
+#define QEDR_GSI_MAX_SEND_WR   (4096)
+
+#define QEDR_GSI_MAX_RECV_SGE  (1)     /* LL2 FW limitation */
+
+#define ETH_P_ROCE             (0x8915)
+#define QEDR_ROCE_V2_UDP_SPORT (0000)
+
+static inline u32 qedr_get_ipv4_from_gid(u8 *gid)
+{
+       return *(u32 *)(void *)&gid[12];
+}
+
+/* RDMA CM */
+int qedr_gsi_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc);
+int qedr_gsi_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+                      struct ib_recv_wr **bad_wr);
+int qedr_gsi_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                      struct ib_send_wr **bad_wr);
+struct ib_qp *qedr_create_gsi_qp(struct qedr_dev *dev,
+                                struct ib_qp_init_attr *attrs,
+                                struct qedr_qp *qp);
+void qedr_store_gsi_qp_cq(struct qedr_dev *dev,
+                         struct qedr_qp *qp, struct ib_qp_init_attr *attrs);
+int qedr_destroy_gsi_qp(struct qedr_dev *dev);
+void qedr_inc_sw_gsi_cons(struct qedr_qp_hwq_info *info);
+#endif
diff --git a/drivers/infiniband/hw/qedr/qedr_hsi.h b/drivers/infiniband/hw/qedr/qedr_hsi.h
new file mode 100644 (file)
index 0000000..66d2752
--- /dev/null
@@ -0,0 +1,56 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed 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, or the
+ * OpenIB.org BSD license below:
+ *
+ *     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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __QED_HSI_ROCE__
+#define __QED_HSI_ROCE__
+
+#include <linux/qed/common_hsi.h>
+#include <linux/qed/roce_common.h>
+#include "qedr_hsi_rdma.h"
+
+/* Affiliated asynchronous events / errors enumeration */
+enum roce_async_events_type {
+       ROCE_ASYNC_EVENT_NONE = 0,
+       ROCE_ASYNC_EVENT_COMM_EST = 1,
+       ROCE_ASYNC_EVENT_SQ_DRAINED,
+       ROCE_ASYNC_EVENT_SRQ_LIMIT,
+       ROCE_ASYNC_EVENT_LAST_WQE_REACHED,
+       ROCE_ASYNC_EVENT_CQ_ERR,
+       ROCE_ASYNC_EVENT_LOCAL_INVALID_REQUEST_ERR,
+       ROCE_ASYNC_EVENT_LOCAL_CATASTROPHIC_ERR,
+       ROCE_ASYNC_EVENT_LOCAL_ACCESS_ERR,
+       ROCE_ASYNC_EVENT_QP_CATASTROPHIC_ERR,
+       ROCE_ASYNC_EVENT_CQ_OVERFLOW_ERR,
+       ROCE_ASYNC_EVENT_SRQ_EMPTY,
+       MAX_ROCE_ASYNC_EVENTS_TYPE
+};
+
+#endif /* __QED_HSI_ROCE__ */
diff --git a/drivers/infiniband/hw/qedr/qedr_hsi_rdma.h b/drivers/infiniband/hw/qedr/qedr_hsi_rdma.h
new file mode 100644 (file)
index 0000000..5c98d20
--- /dev/null
@@ -0,0 +1,748 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed 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, or the
+ * OpenIB.org BSD license below:
+ *
+ *     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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __QED_HSI_RDMA__
+#define __QED_HSI_RDMA__
+
+#include <linux/qed/rdma_common.h>
+
+/* rdma completion notification queue element */
+struct rdma_cnqe {
+       struct regpair  cq_handle;
+};
+
+struct rdma_cqe_responder {
+       struct regpair srq_wr_id;
+       struct regpair qp_handle;
+       __le32 imm_data_or_inv_r_Key;
+       __le32 length;
+       __le32 imm_data_hi;
+       __le16 rq_cons;
+       u8 flags;
+#define RDMA_CQE_RESPONDER_TOGGLE_BIT_MASK  0x1
+#define RDMA_CQE_RESPONDER_TOGGLE_BIT_SHIFT 0
+#define RDMA_CQE_RESPONDER_TYPE_MASK        0x3
+#define RDMA_CQE_RESPONDER_TYPE_SHIFT       1
+#define RDMA_CQE_RESPONDER_INV_FLG_MASK     0x1
+#define RDMA_CQE_RESPONDER_INV_FLG_SHIFT    3
+#define RDMA_CQE_RESPONDER_IMM_FLG_MASK     0x1
+#define RDMA_CQE_RESPONDER_IMM_FLG_SHIFT    4
+#define RDMA_CQE_RESPONDER_RDMA_FLG_MASK    0x1
+#define RDMA_CQE_RESPONDER_RDMA_FLG_SHIFT   5
+#define RDMA_CQE_RESPONDER_RESERVED2_MASK   0x3
+#define RDMA_CQE_RESPONDER_RESERVED2_SHIFT  6
+       u8 status;
+};
+
+struct rdma_cqe_requester {
+       __le16 sq_cons;
+       __le16 reserved0;
+       __le32 reserved1;
+       struct regpair qp_handle;
+       struct regpair reserved2;
+       __le32 reserved3;
+       __le16 reserved4;
+       u8 flags;
+#define RDMA_CQE_REQUESTER_TOGGLE_BIT_MASK  0x1
+#define RDMA_CQE_REQUESTER_TOGGLE_BIT_SHIFT 0
+#define RDMA_CQE_REQUESTER_TYPE_MASK        0x3
+#define RDMA_CQE_REQUESTER_TYPE_SHIFT       1
+#define RDMA_CQE_REQUESTER_RESERVED5_MASK   0x1F
+#define RDMA_CQE_REQUESTER_RESERVED5_SHIFT  3
+       u8 status;
+};
+
+struct rdma_cqe_common {
+       struct regpair reserved0;
+       struct regpair qp_handle;
+       __le16 reserved1[7];
+       u8 flags;
+#define RDMA_CQE_COMMON_TOGGLE_BIT_MASK  0x1
+#define RDMA_CQE_COMMON_TOGGLE_BIT_SHIFT 0
+#define RDMA_CQE_COMMON_TYPE_MASK        0x3
+#define RDMA_CQE_COMMON_TYPE_SHIFT       1
+#define RDMA_CQE_COMMON_RESERVED2_MASK   0x1F
+#define RDMA_CQE_COMMON_RESERVED2_SHIFT  3
+       u8 status;
+};
+
+/* rdma completion queue element */
+union rdma_cqe {
+       struct rdma_cqe_responder resp;
+       struct rdma_cqe_requester req;
+       struct rdma_cqe_common cmn;
+};
+
+/* * CQE requester status enumeration */
+enum rdma_cqe_requester_status_enum {
+       RDMA_CQE_REQ_STS_OK,
+       RDMA_CQE_REQ_STS_BAD_RESPONSE_ERR,
+       RDMA_CQE_REQ_STS_LOCAL_LENGTH_ERR,
+       RDMA_CQE_REQ_STS_LOCAL_QP_OPERATION_ERR,
+       RDMA_CQE_REQ_STS_LOCAL_PROTECTION_ERR,
+       RDMA_CQE_REQ_STS_MEMORY_MGT_OPERATION_ERR,
+       RDMA_CQE_REQ_STS_REMOTE_INVALID_REQUEST_ERR,
+       RDMA_CQE_REQ_STS_REMOTE_ACCESS_ERR,
+       RDMA_CQE_REQ_STS_REMOTE_OPERATION_ERR,
+       RDMA_CQE_REQ_STS_RNR_NAK_RETRY_CNT_ERR,
+       RDMA_CQE_REQ_STS_TRANSPORT_RETRY_CNT_ERR,
+       RDMA_CQE_REQ_STS_WORK_REQUEST_FLUSHED_ERR,
+       MAX_RDMA_CQE_REQUESTER_STATUS_ENUM
+};
+
+/* CQE responder status enumeration */
+enum rdma_cqe_responder_status_enum {
+       RDMA_CQE_RESP_STS_OK,
+       RDMA_CQE_RESP_STS_LOCAL_ACCESS_ERR,
+       RDMA_CQE_RESP_STS_LOCAL_LENGTH_ERR,
+       RDMA_CQE_RESP_STS_LOCAL_QP_OPERATION_ERR,
+       RDMA_CQE_RESP_STS_LOCAL_PROTECTION_ERR,
+       RDMA_CQE_RESP_STS_MEMORY_MGT_OPERATION_ERR,
+       RDMA_CQE_RESP_STS_REMOTE_INVALID_REQUEST_ERR,
+       RDMA_CQE_RESP_STS_WORK_REQUEST_FLUSHED_ERR,
+       MAX_RDMA_CQE_RESPONDER_STATUS_ENUM
+};
+
+/* CQE type enumeration */
+enum rdma_cqe_type {
+       RDMA_CQE_TYPE_REQUESTER,
+       RDMA_CQE_TYPE_RESPONDER_RQ,
+       RDMA_CQE_TYPE_RESPONDER_SRQ,
+       RDMA_CQE_TYPE_INVALID,
+       MAX_RDMA_CQE_TYPE
+};
+
+struct rdma_sq_sge {
+       __le32 length;
+       struct regpair  addr;
+       __le32 l_key;
+};
+
+struct rdma_rq_sge {
+       struct regpair addr;
+       __le32 length;
+       __le32 flags;
+#define RDMA_RQ_SGE_L_KEY_MASK      0x3FFFFFF
+#define RDMA_RQ_SGE_L_KEY_SHIFT     0
+#define RDMA_RQ_SGE_NUM_SGES_MASK   0x7
+#define RDMA_RQ_SGE_NUM_SGES_SHIFT  26
+#define RDMA_RQ_SGE_RESERVED0_MASK  0x7
+#define RDMA_RQ_SGE_RESERVED0_SHIFT 29
+};
+
+struct rdma_srq_sge {
+       struct regpair addr;
+       __le32 length;
+       __le32 l_key;
+};
+
+/* Rdma doorbell data for SQ and RQ */
+struct rdma_pwm_val16_data {
+       __le16 icid;
+       __le16 value;
+};
+
+union rdma_pwm_val16_data_union {
+       struct rdma_pwm_val16_data as_struct;
+       __le32 as_dword;
+};
+
+/* Rdma doorbell data for CQ */
+struct rdma_pwm_val32_data {
+       __le16 icid;
+       u8 agg_flags;
+       u8 params;
+#define RDMA_PWM_VAL32_DATA_AGG_CMD_MASK    0x3
+#define RDMA_PWM_VAL32_DATA_AGG_CMD_SHIFT   0
+#define RDMA_PWM_VAL32_DATA_BYPASS_EN_MASK  0x1
+#define RDMA_PWM_VAL32_DATA_BYPASS_EN_SHIFT 2
+#define RDMA_PWM_VAL32_DATA_RESERVED_MASK   0x1F
+#define RDMA_PWM_VAL32_DATA_RESERVED_SHIFT  3
+       __le32 value;
+};
+
+/* DIF Block size options */
+enum rdma_dif_block_size {
+       RDMA_DIF_BLOCK_512 = 0,
+       RDMA_DIF_BLOCK_4096 = 1,
+       MAX_RDMA_DIF_BLOCK_SIZE
+};
+
+/* DIF CRC initial value */
+enum rdma_dif_crc_seed {
+       RDMA_DIF_CRC_SEED_0000 = 0,
+       RDMA_DIF_CRC_SEED_FFFF = 1,
+       MAX_RDMA_DIF_CRC_SEED
+};
+
+/* RDMA DIF Error Result Structure */
+struct rdma_dif_error_result {
+       __le32 error_intervals;
+       __le32 dif_error_1st_interval;
+       u8 flags;
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_CRC_MASK      0x1
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_CRC_SHIFT     0
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_APP_TAG_MASK  0x1
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_APP_TAG_SHIFT 1
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_REF_TAG_MASK  0x1
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_REF_TAG_SHIFT 2
+#define RDMA_DIF_ERROR_RESULT_RESERVED0_MASK               0xF
+#define RDMA_DIF_ERROR_RESULT_RESERVED0_SHIFT              3
+#define RDMA_DIF_ERROR_RESULT_TOGGLE_BIT_MASK              0x1
+#define RDMA_DIF_ERROR_RESULT_TOGGLE_BIT_SHIFT             7
+       u8 reserved1[55];
+};
+
+/* DIF IO direction */
+enum rdma_dif_io_direction_flg {
+       RDMA_DIF_DIR_RX = 0,
+       RDMA_DIF_DIR_TX = 1,
+       MAX_RDMA_DIF_IO_DIRECTION_FLG
+};
+
+/* RDMA DIF Runt Result Structure */
+struct rdma_dif_runt_result {
+       __le16 guard_tag;
+       __le16 reserved[3];
+};
+
+/* Memory window type enumeration */
+enum rdma_mw_type {
+       RDMA_MW_TYPE_1,
+       RDMA_MW_TYPE_2A,
+       MAX_RDMA_MW_TYPE
+};
+
+struct rdma_sq_atomic_wqe {
+       __le32 reserved1;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_ATOMIC_WQE_COMP_FLG_MASK         0x1
+#define RDMA_SQ_ATOMIC_WQE_COMP_FLG_SHIFT        0
+#define RDMA_SQ_ATOMIC_WQE_RD_FENCE_FLG_MASK     0x1
+#define RDMA_SQ_ATOMIC_WQE_RD_FENCE_FLG_SHIFT    1
+#define RDMA_SQ_ATOMIC_WQE_INV_FENCE_FLG_MASK    0x1
+#define RDMA_SQ_ATOMIC_WQE_INV_FENCE_FLG_SHIFT   2
+#define RDMA_SQ_ATOMIC_WQE_SE_FLG_MASK           0x1
+#define RDMA_SQ_ATOMIC_WQE_SE_FLG_SHIFT          3
+#define RDMA_SQ_ATOMIC_WQE_INLINE_FLG_MASK       0x1
+#define RDMA_SQ_ATOMIC_WQE_INLINE_FLG_SHIFT      4
+#define RDMA_SQ_ATOMIC_WQE_DIF_ON_HOST_FLG_MASK  0x1
+#define RDMA_SQ_ATOMIC_WQE_DIF_ON_HOST_FLG_SHIFT 5
+#define RDMA_SQ_ATOMIC_WQE_RESERVED0_MASK        0x3
+#define RDMA_SQ_ATOMIC_WQE_RESERVED0_SHIFT       6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+       struct regpair remote_va;
+       __le32 r_key;
+       __le32 reserved2;
+       struct regpair cmp_data;
+       struct regpair swap_data;
+};
+
+/* First element (16 bytes) of atomic wqe */
+struct rdma_sq_atomic_wqe_1st {
+       __le32 reserved1;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_ATOMIC_WQE_1ST_COMP_FLG_MASK       0x1
+#define RDMA_SQ_ATOMIC_WQE_1ST_COMP_FLG_SHIFT      0
+#define RDMA_SQ_ATOMIC_WQE_1ST_RD_FENCE_FLG_MASK   0x1
+#define RDMA_SQ_ATOMIC_WQE_1ST_RD_FENCE_FLG_SHIFT  1
+#define RDMA_SQ_ATOMIC_WQE_1ST_INV_FENCE_FLG_MASK  0x1
+#define RDMA_SQ_ATOMIC_WQE_1ST_INV_FENCE_FLG_SHIFT 2
+#define RDMA_SQ_ATOMIC_WQE_1ST_SE_FLG_MASK         0x1
+#define RDMA_SQ_ATOMIC_WQE_1ST_SE_FLG_SHIFT        3
+#define RDMA_SQ_ATOMIC_WQE_1ST_INLINE_FLG_MASK     0x1
+#define RDMA_SQ_ATOMIC_WQE_1ST_INLINE_FLG_SHIFT    4
+#define RDMA_SQ_ATOMIC_WQE_1ST_RESERVED0_MASK      0x7
+#define RDMA_SQ_ATOMIC_WQE_1ST_RESERVED0_SHIFT     5
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+/* Second element (16 bytes) of atomic wqe */
+struct rdma_sq_atomic_wqe_2nd {
+       struct regpair remote_va;
+       __le32 r_key;
+       __le32 reserved2;
+};
+
+/* Third element (16 bytes) of atomic wqe */
+struct rdma_sq_atomic_wqe_3rd {
+       struct regpair cmp_data;
+       struct regpair swap_data;
+};
+
+struct rdma_sq_bind_wqe {
+       struct regpair addr;
+       __le32 l_key;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_BIND_WQE_COMP_FLG_MASK       0x1
+#define RDMA_SQ_BIND_WQE_COMP_FLG_SHIFT      0
+#define RDMA_SQ_BIND_WQE_RD_FENCE_FLG_MASK   0x1
+#define RDMA_SQ_BIND_WQE_RD_FENCE_FLG_SHIFT  1
+#define RDMA_SQ_BIND_WQE_INV_FENCE_FLG_MASK  0x1
+#define RDMA_SQ_BIND_WQE_INV_FENCE_FLG_SHIFT 2
+#define RDMA_SQ_BIND_WQE_SE_FLG_MASK         0x1
+#define RDMA_SQ_BIND_WQE_SE_FLG_SHIFT        3
+#define RDMA_SQ_BIND_WQE_INLINE_FLG_MASK     0x1
+#define RDMA_SQ_BIND_WQE_INLINE_FLG_SHIFT    4
+#define RDMA_SQ_BIND_WQE_RESERVED0_MASK      0x7
+#define RDMA_SQ_BIND_WQE_RESERVED0_SHIFT     5
+       u8 wqe_size;
+       u8 prev_wqe_size;
+       u8 bind_ctrl;
+#define RDMA_SQ_BIND_WQE_ZERO_BASED_MASK     0x1
+#define RDMA_SQ_BIND_WQE_ZERO_BASED_SHIFT    0
+#define RDMA_SQ_BIND_WQE_MW_TYPE_MASK        0x1
+#define RDMA_SQ_BIND_WQE_MW_TYPE_SHIFT       1
+#define RDMA_SQ_BIND_WQE_RESERVED1_MASK      0x3F
+#define RDMA_SQ_BIND_WQE_RESERVED1_SHIFT     2
+       u8 access_ctrl;
+#define RDMA_SQ_BIND_WQE_REMOTE_READ_MASK    0x1
+#define RDMA_SQ_BIND_WQE_REMOTE_READ_SHIFT   0
+#define RDMA_SQ_BIND_WQE_REMOTE_WRITE_MASK   0x1
+#define RDMA_SQ_BIND_WQE_REMOTE_WRITE_SHIFT  1
+#define RDMA_SQ_BIND_WQE_ENABLE_ATOMIC_MASK  0x1
+#define RDMA_SQ_BIND_WQE_ENABLE_ATOMIC_SHIFT 2
+#define RDMA_SQ_BIND_WQE_LOCAL_READ_MASK     0x1
+#define RDMA_SQ_BIND_WQE_LOCAL_READ_SHIFT    3
+#define RDMA_SQ_BIND_WQE_LOCAL_WRITE_MASK    0x1
+#define RDMA_SQ_BIND_WQE_LOCAL_WRITE_SHIFT   4
+#define RDMA_SQ_BIND_WQE_RESERVED2_MASK      0x7
+#define RDMA_SQ_BIND_WQE_RESERVED2_SHIFT     5
+       u8 reserved3;
+       u8 length_hi;
+       __le32 length_lo;
+       __le32 parent_l_key;
+       __le32 reserved4;
+};
+
+/* First element (16 bytes) of bind wqe */
+struct rdma_sq_bind_wqe_1st {
+       struct regpair addr;
+       __le32 l_key;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_BIND_WQE_1ST_COMP_FLG_MASK       0x1
+#define RDMA_SQ_BIND_WQE_1ST_COMP_FLG_SHIFT      0
+#define RDMA_SQ_BIND_WQE_1ST_RD_FENCE_FLG_MASK   0x1
+#define RDMA_SQ_BIND_WQE_1ST_RD_FENCE_FLG_SHIFT  1
+#define RDMA_SQ_BIND_WQE_1ST_INV_FENCE_FLG_MASK  0x1
+#define RDMA_SQ_BIND_WQE_1ST_INV_FENCE_FLG_SHIFT 2
+#define RDMA_SQ_BIND_WQE_1ST_SE_FLG_MASK         0x1
+#define RDMA_SQ_BIND_WQE_1ST_SE_FLG_SHIFT        3
+#define RDMA_SQ_BIND_WQE_1ST_INLINE_FLG_MASK     0x1
+#define RDMA_SQ_BIND_WQE_1ST_INLINE_FLG_SHIFT    4
+#define RDMA_SQ_BIND_WQE_1ST_RESERVED0_MASK      0x7
+#define RDMA_SQ_BIND_WQE_1ST_RESERVED0_SHIFT     5
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+/* Second element (16 bytes) of bind wqe */
+struct rdma_sq_bind_wqe_2nd {
+       u8 bind_ctrl;
+#define RDMA_SQ_BIND_WQE_2ND_ZERO_BASED_MASK     0x1
+#define RDMA_SQ_BIND_WQE_2ND_ZERO_BASED_SHIFT    0
+#define RDMA_SQ_BIND_WQE_2ND_MW_TYPE_MASK        0x1
+#define RDMA_SQ_BIND_WQE_2ND_MW_TYPE_SHIFT       1
+#define RDMA_SQ_BIND_WQE_2ND_RESERVED1_MASK      0x3F
+#define RDMA_SQ_BIND_WQE_2ND_RESERVED1_SHIFT     2
+       u8 access_ctrl;
+#define RDMA_SQ_BIND_WQE_2ND_REMOTE_READ_MASK    0x1
+#define RDMA_SQ_BIND_WQE_2ND_REMOTE_READ_SHIFT   0
+#define RDMA_SQ_BIND_WQE_2ND_REMOTE_WRITE_MASK   0x1
+#define RDMA_SQ_BIND_WQE_2ND_REMOTE_WRITE_SHIFT  1
+#define RDMA_SQ_BIND_WQE_2ND_ENABLE_ATOMIC_MASK  0x1
+#define RDMA_SQ_BIND_WQE_2ND_ENABLE_ATOMIC_SHIFT 2
+#define RDMA_SQ_BIND_WQE_2ND_LOCAL_READ_MASK     0x1
+#define RDMA_SQ_BIND_WQE_2ND_LOCAL_READ_SHIFT    3
+#define RDMA_SQ_BIND_WQE_2ND_LOCAL_WRITE_MASK    0x1
+#define RDMA_SQ_BIND_WQE_2ND_LOCAL_WRITE_SHIFT   4
+#define RDMA_SQ_BIND_WQE_2ND_RESERVED2_MASK      0x7
+#define RDMA_SQ_BIND_WQE_2ND_RESERVED2_SHIFT     5
+       u8 reserved3;
+       u8 length_hi;
+       __le32 length_lo;
+       __le32 parent_l_key;
+       __le32 reserved4;
+};
+
+/* Structure with only the SQ WQE common
+ * fields. Size is of one SQ element (16B)
+ */
+struct rdma_sq_common_wqe {
+       __le32 reserved1[3];
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_COMMON_WQE_COMP_FLG_MASK       0x1
+#define RDMA_SQ_COMMON_WQE_COMP_FLG_SHIFT      0
+#define RDMA_SQ_COMMON_WQE_RD_FENCE_FLG_MASK   0x1
+#define RDMA_SQ_COMMON_WQE_RD_FENCE_FLG_SHIFT  1
+#define RDMA_SQ_COMMON_WQE_INV_FENCE_FLG_MASK  0x1
+#define RDMA_SQ_COMMON_WQE_INV_FENCE_FLG_SHIFT 2
+#define RDMA_SQ_COMMON_WQE_SE_FLG_MASK         0x1
+#define RDMA_SQ_COMMON_WQE_SE_FLG_SHIFT        3
+#define RDMA_SQ_COMMON_WQE_INLINE_FLG_MASK     0x1
+#define RDMA_SQ_COMMON_WQE_INLINE_FLG_SHIFT    4
+#define RDMA_SQ_COMMON_WQE_RESERVED0_MASK      0x7
+#define RDMA_SQ_COMMON_WQE_RESERVED0_SHIFT     5
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+struct rdma_sq_fmr_wqe {
+       struct regpair addr;
+       __le32 l_key;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_FMR_WQE_COMP_FLG_MASK                0x1
+#define RDMA_SQ_FMR_WQE_COMP_FLG_SHIFT               0
+#define RDMA_SQ_FMR_WQE_RD_FENCE_FLG_MASK            0x1
+#define RDMA_SQ_FMR_WQE_RD_FENCE_FLG_SHIFT           1
+#define RDMA_SQ_FMR_WQE_INV_FENCE_FLG_MASK           0x1
+#define RDMA_SQ_FMR_WQE_INV_FENCE_FLG_SHIFT          2
+#define RDMA_SQ_FMR_WQE_SE_FLG_MASK                  0x1
+#define RDMA_SQ_FMR_WQE_SE_FLG_SHIFT                 3
+#define RDMA_SQ_FMR_WQE_INLINE_FLG_MASK              0x1
+#define RDMA_SQ_FMR_WQE_INLINE_FLG_SHIFT             4
+#define RDMA_SQ_FMR_WQE_DIF_ON_HOST_FLG_MASK         0x1
+#define RDMA_SQ_FMR_WQE_DIF_ON_HOST_FLG_SHIFT        5
+#define RDMA_SQ_FMR_WQE_RESERVED0_MASK               0x3
+#define RDMA_SQ_FMR_WQE_RESERVED0_SHIFT              6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+       u8 fmr_ctrl;
+#define RDMA_SQ_FMR_WQE_PAGE_SIZE_LOG_MASK           0x1F
+#define RDMA_SQ_FMR_WQE_PAGE_SIZE_LOG_SHIFT          0
+#define RDMA_SQ_FMR_WQE_ZERO_BASED_MASK              0x1
+#define RDMA_SQ_FMR_WQE_ZERO_BASED_SHIFT             5
+#define RDMA_SQ_FMR_WQE_BIND_EN_MASK                 0x1
+#define RDMA_SQ_FMR_WQE_BIND_EN_SHIFT                6
+#define RDMA_SQ_FMR_WQE_RESERVED1_MASK               0x1
+#define RDMA_SQ_FMR_WQE_RESERVED1_SHIFT              7
+       u8 access_ctrl;
+#define RDMA_SQ_FMR_WQE_REMOTE_READ_MASK             0x1
+#define RDMA_SQ_FMR_WQE_REMOTE_READ_SHIFT            0
+#define RDMA_SQ_FMR_WQE_REMOTE_WRITE_MASK            0x1
+#define RDMA_SQ_FMR_WQE_REMOTE_WRITE_SHIFT           1
+#define RDMA_SQ_FMR_WQE_ENABLE_ATOMIC_MASK           0x1
+#define RDMA_SQ_FMR_WQE_ENABLE_ATOMIC_SHIFT          2
+#define RDMA_SQ_FMR_WQE_LOCAL_READ_MASK              0x1
+#define RDMA_SQ_FMR_WQE_LOCAL_READ_SHIFT             3
+#define RDMA_SQ_FMR_WQE_LOCAL_WRITE_MASK             0x1
+#define RDMA_SQ_FMR_WQE_LOCAL_WRITE_SHIFT            4
+#define RDMA_SQ_FMR_WQE_RESERVED2_MASK               0x7
+#define RDMA_SQ_FMR_WQE_RESERVED2_SHIFT              5
+       u8 reserved3;
+       u8 length_hi;
+       __le32 length_lo;
+       struct regpair pbl_addr;
+       __le32 dif_base_ref_tag;
+       __le16 dif_app_tag;
+       __le16 dif_app_tag_mask;
+       __le16 dif_runt_crc_value;
+       __le16 dif_flags;
+#define RDMA_SQ_FMR_WQE_DIF_IO_DIRECTION_FLG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_DIF_IO_DIRECTION_FLG_SHIFT   0
+#define RDMA_SQ_FMR_WQE_DIF_BLOCK_SIZE_MASK          0x1
+#define RDMA_SQ_FMR_WQE_DIF_BLOCK_SIZE_SHIFT         1
+#define RDMA_SQ_FMR_WQE_DIF_RUNT_VALID_FLG_MASK      0x1
+#define RDMA_SQ_FMR_WQE_DIF_RUNT_VALID_FLG_SHIFT     2
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_CRC_GUARD_MASK  0x1
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_CRC_GUARD_SHIFT 3
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_REF_TAG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_REF_TAG_SHIFT   4
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_APP_TAG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_APP_TAG_SHIFT   5
+#define RDMA_SQ_FMR_WQE_DIF_CRC_SEED_MASK            0x1
+#define RDMA_SQ_FMR_WQE_DIF_CRC_SEED_SHIFT           6
+#define RDMA_SQ_FMR_WQE_RESERVED4_MASK               0x1FF
+#define RDMA_SQ_FMR_WQE_RESERVED4_SHIFT              7
+       __le32 Reserved5;
+};
+
+/* First element (16 bytes) of fmr wqe */
+struct rdma_sq_fmr_wqe_1st {
+       struct regpair addr;
+       __le32 l_key;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_FMR_WQE_1ST_COMP_FLG_MASK         0x1
+#define RDMA_SQ_FMR_WQE_1ST_COMP_FLG_SHIFT        0
+#define RDMA_SQ_FMR_WQE_1ST_RD_FENCE_FLG_MASK     0x1
+#define RDMA_SQ_FMR_WQE_1ST_RD_FENCE_FLG_SHIFT    1
+#define RDMA_SQ_FMR_WQE_1ST_INV_FENCE_FLG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_1ST_INV_FENCE_FLG_SHIFT   2
+#define RDMA_SQ_FMR_WQE_1ST_SE_FLG_MASK           0x1
+#define RDMA_SQ_FMR_WQE_1ST_SE_FLG_SHIFT          3
+#define RDMA_SQ_FMR_WQE_1ST_INLINE_FLG_MASK       0x1
+#define RDMA_SQ_FMR_WQE_1ST_INLINE_FLG_SHIFT      4
+#define RDMA_SQ_FMR_WQE_1ST_DIF_ON_HOST_FLG_MASK  0x1
+#define RDMA_SQ_FMR_WQE_1ST_DIF_ON_HOST_FLG_SHIFT 5
+#define RDMA_SQ_FMR_WQE_1ST_RESERVED0_MASK        0x3
+#define RDMA_SQ_FMR_WQE_1ST_RESERVED0_SHIFT       6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+/* Second element (16 bytes) of fmr wqe */
+struct rdma_sq_fmr_wqe_2nd {
+       u8 fmr_ctrl;
+#define RDMA_SQ_FMR_WQE_2ND_PAGE_SIZE_LOG_MASK  0x1F
+#define RDMA_SQ_FMR_WQE_2ND_PAGE_SIZE_LOG_SHIFT 0
+#define RDMA_SQ_FMR_WQE_2ND_ZERO_BASED_MASK     0x1
+#define RDMA_SQ_FMR_WQE_2ND_ZERO_BASED_SHIFT    5
+#define RDMA_SQ_FMR_WQE_2ND_BIND_EN_MASK        0x1
+#define RDMA_SQ_FMR_WQE_2ND_BIND_EN_SHIFT       6
+#define RDMA_SQ_FMR_WQE_2ND_RESERVED1_MASK      0x1
+#define RDMA_SQ_FMR_WQE_2ND_RESERVED1_SHIFT     7
+       u8 access_ctrl;
+#define RDMA_SQ_FMR_WQE_2ND_REMOTE_READ_MASK    0x1
+#define RDMA_SQ_FMR_WQE_2ND_REMOTE_READ_SHIFT   0
+#define RDMA_SQ_FMR_WQE_2ND_REMOTE_WRITE_MASK   0x1
+#define RDMA_SQ_FMR_WQE_2ND_REMOTE_WRITE_SHIFT  1
+#define RDMA_SQ_FMR_WQE_2ND_ENABLE_ATOMIC_MASK  0x1
+#define RDMA_SQ_FMR_WQE_2ND_ENABLE_ATOMIC_SHIFT 2
+#define RDMA_SQ_FMR_WQE_2ND_LOCAL_READ_MASK     0x1
+#define RDMA_SQ_FMR_WQE_2ND_LOCAL_READ_SHIFT    3
+#define RDMA_SQ_FMR_WQE_2ND_LOCAL_WRITE_MASK    0x1
+#define RDMA_SQ_FMR_WQE_2ND_LOCAL_WRITE_SHIFT   4
+#define RDMA_SQ_FMR_WQE_2ND_RESERVED2_MASK      0x7
+#define RDMA_SQ_FMR_WQE_2ND_RESERVED2_SHIFT     5
+       u8 reserved3;
+       u8 length_hi;
+       __le32 length_lo;
+       struct regpair pbl_addr;
+};
+
+/* Third element (16 bytes) of fmr wqe */
+struct rdma_sq_fmr_wqe_3rd {
+       __le32 dif_base_ref_tag;
+       __le16 dif_app_tag;
+       __le16 dif_app_tag_mask;
+       __le16 dif_runt_crc_value;
+       __le16 dif_flags;
+#define RDMA_SQ_FMR_WQE_3RD_DIF_IO_DIRECTION_FLG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_IO_DIRECTION_FLG_SHIFT   0
+#define RDMA_SQ_FMR_WQE_3RD_DIF_BLOCK_SIZE_MASK          0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_BLOCK_SIZE_SHIFT         1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_RUNT_VALID_FLG_MASK      0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_RUNT_VALID_FLG_SHIFT     2
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_CRC_GUARD_MASK  0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_CRC_GUARD_SHIFT 3
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_REF_TAG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_REF_TAG_SHIFT   4
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_APP_TAG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_APP_TAG_SHIFT   5
+#define RDMA_SQ_FMR_WQE_3RD_DIF_CRC_SEED_MASK            0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_CRC_SEED_SHIFT           6
+#define RDMA_SQ_FMR_WQE_3RD_RESERVED4_MASK               0x1FF
+#define RDMA_SQ_FMR_WQE_3RD_RESERVED4_SHIFT              7
+       __le32 Reserved5;
+};
+
+struct rdma_sq_local_inv_wqe {
+       struct regpair reserved;
+       __le32 inv_l_key;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_LOCAL_INV_WQE_COMP_FLG_MASK         0x1
+#define RDMA_SQ_LOCAL_INV_WQE_COMP_FLG_SHIFT        0
+#define RDMA_SQ_LOCAL_INV_WQE_RD_FENCE_FLG_MASK     0x1
+#define RDMA_SQ_LOCAL_INV_WQE_RD_FENCE_FLG_SHIFT    1
+#define RDMA_SQ_LOCAL_INV_WQE_INV_FENCE_FLG_MASK    0x1
+#define RDMA_SQ_LOCAL_INV_WQE_INV_FENCE_FLG_SHIFT   2
+#define RDMA_SQ_LOCAL_INV_WQE_SE_FLG_MASK           0x1
+#define RDMA_SQ_LOCAL_INV_WQE_SE_FLG_SHIFT          3
+#define RDMA_SQ_LOCAL_INV_WQE_INLINE_FLG_MASK       0x1
+#define RDMA_SQ_LOCAL_INV_WQE_INLINE_FLG_SHIFT      4
+#define RDMA_SQ_LOCAL_INV_WQE_DIF_ON_HOST_FLG_MASK  0x1
+#define RDMA_SQ_LOCAL_INV_WQE_DIF_ON_HOST_FLG_SHIFT 5
+#define RDMA_SQ_LOCAL_INV_WQE_RESERVED0_MASK        0x3
+#define RDMA_SQ_LOCAL_INV_WQE_RESERVED0_SHIFT       6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+struct rdma_sq_rdma_wqe {
+       __le32 imm_data;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_RDMA_WQE_COMP_FLG_MASK                  0x1
+#define RDMA_SQ_RDMA_WQE_COMP_FLG_SHIFT                 0
+#define RDMA_SQ_RDMA_WQE_RD_FENCE_FLG_MASK              0x1
+#define RDMA_SQ_RDMA_WQE_RD_FENCE_FLG_SHIFT             1
+#define RDMA_SQ_RDMA_WQE_INV_FENCE_FLG_MASK             0x1
+#define RDMA_SQ_RDMA_WQE_INV_FENCE_FLG_SHIFT            2
+#define RDMA_SQ_RDMA_WQE_SE_FLG_MASK                    0x1
+#define RDMA_SQ_RDMA_WQE_SE_FLG_SHIFT                   3
+#define RDMA_SQ_RDMA_WQE_INLINE_FLG_MASK                0x1
+#define RDMA_SQ_RDMA_WQE_INLINE_FLG_SHIFT               4
+#define RDMA_SQ_RDMA_WQE_DIF_ON_HOST_FLG_MASK           0x1
+#define RDMA_SQ_RDMA_WQE_DIF_ON_HOST_FLG_SHIFT          5
+#define RDMA_SQ_RDMA_WQE_RESERVED0_MASK                 0x3
+#define RDMA_SQ_RDMA_WQE_RESERVED0_SHIFT                6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+       struct regpair remote_va;
+       __le32 r_key;
+       u8 dif_flags;
+#define RDMA_SQ_RDMA_WQE_DIF_BLOCK_SIZE_MASK            0x1
+#define RDMA_SQ_RDMA_WQE_DIF_BLOCK_SIZE_SHIFT           0
+#define RDMA_SQ_RDMA_WQE_DIF_FIRST_RDMA_IN_IO_FLG_MASK  0x1
+#define RDMA_SQ_RDMA_WQE_DIF_FIRST_RDMA_IN_IO_FLG_SHIFT 1
+#define RDMA_SQ_RDMA_WQE_DIF_LAST_RDMA_IN_IO_FLG_MASK   0x1
+#define RDMA_SQ_RDMA_WQE_DIF_LAST_RDMA_IN_IO_FLG_SHIFT  2
+#define RDMA_SQ_RDMA_WQE_RESERVED1_MASK                 0x1F
+#define RDMA_SQ_RDMA_WQE_RESERVED1_SHIFT                3
+       u8 reserved2[3];
+};
+
+/* First element (16 bytes) of rdma wqe */
+struct rdma_sq_rdma_wqe_1st {
+       __le32 imm_data;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_RDMA_WQE_1ST_COMP_FLG_MASK         0x1
+#define RDMA_SQ_RDMA_WQE_1ST_COMP_FLG_SHIFT        0
+#define RDMA_SQ_RDMA_WQE_1ST_RD_FENCE_FLG_MASK     0x1
+#define RDMA_SQ_RDMA_WQE_1ST_RD_FENCE_FLG_SHIFT    1
+#define RDMA_SQ_RDMA_WQE_1ST_INV_FENCE_FLG_MASK    0x1
+#define RDMA_SQ_RDMA_WQE_1ST_INV_FENCE_FLG_SHIFT   2
+#define RDMA_SQ_RDMA_WQE_1ST_SE_FLG_MASK           0x1
+#define RDMA_SQ_RDMA_WQE_1ST_SE_FLG_SHIFT          3
+#define RDMA_SQ_RDMA_WQE_1ST_INLINE_FLG_MASK       0x1
+#define RDMA_SQ_RDMA_WQE_1ST_INLINE_FLG_SHIFT      4
+#define RDMA_SQ_RDMA_WQE_1ST_DIF_ON_HOST_FLG_MASK  0x1
+#define RDMA_SQ_RDMA_WQE_1ST_DIF_ON_HOST_FLG_SHIFT 5
+#define RDMA_SQ_RDMA_WQE_1ST_RESERVED0_MASK        0x3
+#define RDMA_SQ_RDMA_WQE_1ST_RESERVED0_SHIFT       6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+/* Second element (16 bytes) of rdma wqe */
+struct rdma_sq_rdma_wqe_2nd {
+       struct regpair remote_va;
+       __le32 r_key;
+       u8 dif_flags;
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_BLOCK_SIZE_MASK         0x1
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_BLOCK_SIZE_SHIFT        0
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_FIRST_SEGMENT_FLG_MASK  0x1
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_FIRST_SEGMENT_FLG_SHIFT 1
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_LAST_SEGMENT_FLG_MASK   0x1
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_LAST_SEGMENT_FLG_SHIFT  2
+#define RDMA_SQ_RDMA_WQE_2ND_RESERVED1_MASK              0x1F
+#define RDMA_SQ_RDMA_WQE_2ND_RESERVED1_SHIFT             3
+       u8 reserved2[3];
+};
+
+/* SQ WQE req type enumeration */
+enum rdma_sq_req_type {
+       RDMA_SQ_REQ_TYPE_SEND,
+       RDMA_SQ_REQ_TYPE_SEND_WITH_IMM,
+       RDMA_SQ_REQ_TYPE_SEND_WITH_INVALIDATE,
+       RDMA_SQ_REQ_TYPE_RDMA_WR,
+       RDMA_SQ_REQ_TYPE_RDMA_WR_WITH_IMM,
+       RDMA_SQ_REQ_TYPE_RDMA_RD,
+       RDMA_SQ_REQ_TYPE_ATOMIC_CMP_AND_SWAP,
+       RDMA_SQ_REQ_TYPE_ATOMIC_ADD,
+       RDMA_SQ_REQ_TYPE_LOCAL_INVALIDATE,
+       RDMA_SQ_REQ_TYPE_FAST_MR,
+       RDMA_SQ_REQ_TYPE_BIND,
+       RDMA_SQ_REQ_TYPE_INVALID,
+       MAX_RDMA_SQ_REQ_TYPE
+};
+
+struct rdma_sq_send_wqe {
+       __le32 inv_key_or_imm_data;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_SEND_WQE_COMP_FLG_MASK         0x1
+#define RDMA_SQ_SEND_WQE_COMP_FLG_SHIFT        0
+#define RDMA_SQ_SEND_WQE_RD_FENCE_FLG_MASK     0x1
+#define RDMA_SQ_SEND_WQE_RD_FENCE_FLG_SHIFT    1
+#define RDMA_SQ_SEND_WQE_INV_FENCE_FLG_MASK    0x1
+#define RDMA_SQ_SEND_WQE_INV_FENCE_FLG_SHIFT   2
+#define RDMA_SQ_SEND_WQE_SE_FLG_MASK           0x1
+#define RDMA_SQ_SEND_WQE_SE_FLG_SHIFT          3
+#define RDMA_SQ_SEND_WQE_INLINE_FLG_MASK       0x1
+#define RDMA_SQ_SEND_WQE_INLINE_FLG_SHIFT      4
+#define RDMA_SQ_SEND_WQE_DIF_ON_HOST_FLG_MASK  0x1
+#define RDMA_SQ_SEND_WQE_DIF_ON_HOST_FLG_SHIFT 5
+#define RDMA_SQ_SEND_WQE_RESERVED0_MASK        0x3
+#define RDMA_SQ_SEND_WQE_RESERVED0_SHIFT       6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+       __le32 reserved1[4];
+};
+
+struct rdma_sq_send_wqe_1st {
+       __le32 inv_key_or_imm_data;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_SEND_WQE_1ST_COMP_FLG_MASK       0x1
+#define RDMA_SQ_SEND_WQE_1ST_COMP_FLG_SHIFT      0
+#define RDMA_SQ_SEND_WQE_1ST_RD_FENCE_FLG_MASK   0x1
+#define RDMA_SQ_SEND_WQE_1ST_RD_FENCE_FLG_SHIFT  1
+#define RDMA_SQ_SEND_WQE_1ST_INV_FENCE_FLG_MASK  0x1
+#define RDMA_SQ_SEND_WQE_1ST_INV_FENCE_FLG_SHIFT 2
+#define RDMA_SQ_SEND_WQE_1ST_SE_FLG_MASK         0x1
+#define RDMA_SQ_SEND_WQE_1ST_SE_FLG_SHIFT        3
+#define RDMA_SQ_SEND_WQE_1ST_INLINE_FLG_MASK     0x1
+#define RDMA_SQ_SEND_WQE_1ST_INLINE_FLG_SHIFT    4
+#define RDMA_SQ_SEND_WQE_1ST_RESERVED0_MASK      0x7
+#define RDMA_SQ_SEND_WQE_1ST_RESERVED0_SHIFT     5
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+struct rdma_sq_send_wqe_2st {
+       __le32 reserved1[4];
+};
+
+#endif /* __QED_HSI_RDMA__ */
diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c
new file mode 100644 (file)
index 0000000..a615142
--- /dev/null
@@ -0,0 +1,3547 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed 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, or the
+ * OpenIB.org BSD license below:
+ *
+ *     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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/dma-mapping.h>
+#include <linux/crc32.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
+#include <linux/iommu.h>
+
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_user_verbs.h>
+#include <rdma/iw_cm.h>
+#include <rdma/ib_umem.h>
+#include <rdma/ib_addr.h>
+#include <rdma/ib_cache.h>
+
+#include "qedr_hsi.h"
+#include <linux/qed/qed_if.h>
+#include "qedr.h"
+#include "verbs.h"
+#include <rdma/qedr-abi.h>
+#include "qedr_cm.h"
+
+#define DB_ADDR_SHIFT(addr)            ((addr) << DB_PWM_ADDR_OFFSET_SHIFT)
+
+int qedr_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey)
+{
+       if (index > QEDR_ROCE_PKEY_TABLE_LEN)
+               return -EINVAL;
+
+       *pkey = QEDR_ROCE_PKEY_DEFAULT;
+       return 0;
+}
+
+int qedr_query_gid(struct ib_device *ibdev, u8 port, int index,
+                  union ib_gid *sgid)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+       int rc = 0;
+
+       if (!rdma_cap_roce_gid_table(ibdev, port))
+               return -ENODEV;
+
+       rc = ib_get_cached_gid(ibdev, port, index, sgid, NULL);
+       if (rc == -EAGAIN) {
+               memcpy(sgid, &zgid, sizeof(*sgid));
+               return 0;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "query gid: index=%d %llx:%llx\n", index,
+                sgid->global.interface_id, sgid->global.subnet_prefix);
+
+       return rc;
+}
+
+int qedr_add_gid(struct ib_device *device, u8 port_num,
+                unsigned int index, const union ib_gid *gid,
+                const struct ib_gid_attr *attr, void **context)
+{
+       if (!rdma_cap_roce_gid_table(device, port_num))
+               return -EINVAL;
+
+       if (port_num > QEDR_MAX_PORT)
+               return -EINVAL;
+
+       if (!context)
+               return -EINVAL;
+
+       return 0;
+}
+
+int qedr_del_gid(struct ib_device *device, u8 port_num,
+                unsigned int index, void **context)
+{
+       if (!rdma_cap_roce_gid_table(device, port_num))
+               return -EINVAL;
+
+       if (port_num > QEDR_MAX_PORT)
+               return -EINVAL;
+
+       if (!context)
+               return -EINVAL;
+
+       return 0;
+}
+
+int qedr_query_device(struct ib_device *ibdev,
+                     struct ib_device_attr *attr, struct ib_udata *udata)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+       struct qedr_device_attr *qattr = &dev->attr;
+
+       if (!dev->rdma_ctx) {
+               DP_ERR(dev,
+                      "qedr_query_device called with invalid params rdma_ctx=%p\n",
+                      dev->rdma_ctx);
+               return -EINVAL;
+       }
+
+       memset(attr, 0, sizeof(*attr));
+
+       attr->fw_ver = qattr->fw_ver;
+       attr->sys_image_guid = qattr->sys_image_guid;
+       attr->max_mr_size = qattr->max_mr_size;
+       attr->page_size_cap = qattr->page_size_caps;
+       attr->vendor_id = qattr->vendor_id;
+       attr->vendor_part_id = qattr->vendor_part_id;
+       attr->hw_ver = qattr->hw_ver;
+       attr->max_qp = qattr->max_qp;
+       attr->max_qp_wr = max_t(u32, qattr->max_sqe, qattr->max_rqe);
+       attr->device_cap_flags = IB_DEVICE_CURR_QP_STATE_MOD |
+           IB_DEVICE_RC_RNR_NAK_GEN |
+           IB_DEVICE_LOCAL_DMA_LKEY | IB_DEVICE_MEM_MGT_EXTENSIONS;
+
+       attr->max_sge = qattr->max_sge;
+       attr->max_sge_rd = qattr->max_sge;
+       attr->max_cq = qattr->max_cq;
+       attr->max_cqe = qattr->max_cqe;
+       attr->max_mr = qattr->max_mr;
+       attr->max_mw = qattr->max_mw;
+       attr->max_pd = qattr->max_pd;
+       attr->atomic_cap = dev->atomic_cap;
+       attr->max_fmr = qattr->max_fmr;
+       attr->max_map_per_fmr = 16;
+       attr->max_qp_init_rd_atom =
+           1 << (fls(qattr->max_qp_req_rd_atomic_resc) - 1);
+       attr->max_qp_rd_atom =
+           min(1 << (fls(qattr->max_qp_resp_rd_atomic_resc) - 1),
+               attr->max_qp_init_rd_atom);
+
+       attr->max_srq = qattr->max_srq;
+       attr->max_srq_sge = qattr->max_srq_sge;
+       attr->max_srq_wr = qattr->max_srq_wr;
+
+       attr->local_ca_ack_delay = qattr->dev_ack_delay;
+       attr->max_fast_reg_page_list_len = qattr->max_mr / 8;
+       attr->max_pkeys = QEDR_ROCE_PKEY_MAX;
+       attr->max_ah = qattr->max_ah;
+
+       return 0;
+}
+
+#define QEDR_SPEED_SDR         (1)
+#define QEDR_SPEED_DDR         (2)
+#define QEDR_SPEED_QDR         (4)
+#define QEDR_SPEED_FDR10       (8)
+#define QEDR_SPEED_FDR         (16)
+#define QEDR_SPEED_EDR         (32)
+
+static inline void get_link_speed_and_width(int speed, u8 *ib_speed,
+                                           u8 *ib_width)
+{
+       switch (speed) {
+       case 1000:
+               *ib_speed = QEDR_SPEED_SDR;
+               *ib_width = IB_WIDTH_1X;
+               break;
+       case 10000:
+               *ib_speed = QEDR_SPEED_QDR;
+               *ib_width = IB_WIDTH_1X;
+               break;
+
+       case 20000:
+               *ib_speed = QEDR_SPEED_DDR;
+               *ib_width = IB_WIDTH_4X;
+               break;
+
+       case 25000:
+               *ib_speed = QEDR_SPEED_EDR;
+               *ib_width = IB_WIDTH_1X;
+               break;
+
+       case 40000:
+               *ib_speed = QEDR_SPEED_QDR;
+               *ib_width = IB_WIDTH_4X;
+               break;
+
+       case 50000:
+               *ib_speed = QEDR_SPEED_QDR;
+               *ib_width = IB_WIDTH_4X;
+               break;
+
+       case 100000:
+               *ib_speed = QEDR_SPEED_EDR;
+               *ib_width = IB_WIDTH_4X;
+               break;
+
+       default:
+               /* Unsupported */
+               *ib_speed = QEDR_SPEED_SDR;
+               *ib_width = IB_WIDTH_1X;
+       }
+}
+
+int qedr_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr *attr)
+{
+       struct qedr_dev *dev;
+       struct qed_rdma_port *rdma_port;
+
+       dev = get_qedr_dev(ibdev);
+       if (port > 1) {
+               DP_ERR(dev, "invalid_port=0x%x\n", port);
+               return -EINVAL;
+       }
+
+       if (!dev->rdma_ctx) {
+               DP_ERR(dev, "rdma_ctx is NULL\n");
+               return -EINVAL;
+       }
+
+       rdma_port = dev->ops->rdma_query_port(dev->rdma_ctx);
+       memset(attr, 0, sizeof(*attr));
+
+       if (rdma_port->port_state == QED_RDMA_PORT_UP) {
+               attr->state = IB_PORT_ACTIVE;
+               attr->phys_state = 5;
+       } else {
+               attr->state = IB_PORT_DOWN;
+               attr->phys_state = 3;
+       }
+       attr->max_mtu = IB_MTU_4096;
+       attr->active_mtu = iboe_get_mtu(dev->ndev->mtu);
+       attr->lid = 0;
+       attr->lmc = 0;
+       attr->sm_lid = 0;
+       attr->sm_sl = 0;
+       attr->port_cap_flags = IB_PORT_IP_BASED_GIDS;
+       attr->gid_tbl_len = QEDR_MAX_SGID;
+       attr->pkey_tbl_len = QEDR_ROCE_PKEY_TABLE_LEN;
+       attr->bad_pkey_cntr = rdma_port->pkey_bad_counter;
+       attr->qkey_viol_cntr = 0;
+       get_link_speed_and_width(rdma_port->link_speed,
+                                &attr->active_speed, &attr->active_width);
+       attr->max_msg_sz = rdma_port->max_msg_size;
+       attr->max_vl_num = 4;
+
+       return 0;
+}
+
+int qedr_modify_port(struct ib_device *ibdev, u8 port, int mask,
+                    struct ib_port_modify *props)
+{
+       struct qedr_dev *dev;
+
+       dev = get_qedr_dev(ibdev);
+       if (port > 1) {
+               DP_ERR(dev, "invalid_port=0x%x\n", port);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int qedr_add_mmap(struct qedr_ucontext *uctx, u64 phy_addr,
+                        unsigned long len)
+{
+       struct qedr_mm *mm;
+
+       mm = kzalloc(sizeof(*mm), GFP_KERNEL);
+       if (!mm)
+               return -ENOMEM;
+
+       mm->key.phy_addr = phy_addr;
+       /* This function might be called with a length which is not a multiple
+        * of PAGE_SIZE, while the mapping is PAGE_SIZE grained and the kernel
+        * forces this granularity by increasing the requested size if needed.
+        * When qedr_mmap is called, it will search the list with the updated
+        * length as a key. To prevent search failures, the length is rounded up
+        * in advance to PAGE_SIZE.
+        */
+       mm->key.len = roundup(len, PAGE_SIZE);
+       INIT_LIST_HEAD(&mm->entry);
+
+       mutex_lock(&uctx->mm_list_lock);
+       list_add(&mm->entry, &uctx->mm_head);
+       mutex_unlock(&uctx->mm_list_lock);
+
+       DP_DEBUG(uctx->dev, QEDR_MSG_MISC,
+                "added (addr=0x%llx,len=0x%lx) for ctx=%p\n",
+                (unsigned long long)mm->key.phy_addr,
+                (unsigned long)mm->key.len, uctx);
+
+       return 0;
+}
+
+static bool qedr_search_mmap(struct qedr_ucontext *uctx, u64 phy_addr,
+                            unsigned long len)
+{
+       bool found = false;
+       struct qedr_mm *mm;
+
+       mutex_lock(&uctx->mm_list_lock);
+       list_for_each_entry(mm, &uctx->mm_head, entry) {
+               if (len != mm->key.len || phy_addr != mm->key.phy_addr)
+                       continue;
+
+               found = true;
+               break;
+       }
+       mutex_unlock(&uctx->mm_list_lock);
+       DP_DEBUG(uctx->dev, QEDR_MSG_MISC,
+                "searched for (addr=0x%llx,len=0x%lx) for ctx=%p, result=%d\n",
+                mm->key.phy_addr, mm->key.len, uctx, found);
+
+       return found;
+}
+
+struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *ibdev,
+                                       struct ib_udata *udata)
+{
+       int rc;
+       struct qedr_ucontext *ctx;
+       struct qedr_alloc_ucontext_resp uresp;
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+       struct qed_rdma_add_user_out_params oparams;
+
+       if (!udata)
+               return ERR_PTR(-EFAULT);
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return ERR_PTR(-ENOMEM);
+
+       rc = dev->ops->rdma_add_user(dev->rdma_ctx, &oparams);
+       if (rc) {
+               DP_ERR(dev,
+                      "failed to allocate a DPI for a new RoCE application, rc=%d. To overcome this consider to increase the number of DPIs, increase the doorbell BAR size or just close unnecessary RoCE applications. In order to increase the number of DPIs consult the qedr readme\n",
+                      rc);
+               goto err;
+       }
+
+       ctx->dpi = oparams.dpi;
+       ctx->dpi_addr = oparams.dpi_addr;
+       ctx->dpi_phys_addr = oparams.dpi_phys_addr;
+       ctx->dpi_size = oparams.dpi_size;
+       INIT_LIST_HEAD(&ctx->mm_head);
+       mutex_init(&ctx->mm_list_lock);
+
+       memset(&uresp, 0, sizeof(uresp));
+
+       uresp.db_pa = ctx->dpi_phys_addr;
+       uresp.db_size = ctx->dpi_size;
+       uresp.max_send_wr = dev->attr.max_sqe;
+       uresp.max_recv_wr = dev->attr.max_rqe;
+       uresp.max_srq_wr = dev->attr.max_srq_wr;
+       uresp.sges_per_send_wr = QEDR_MAX_SQE_ELEMENTS_PER_SQE;
+       uresp.sges_per_recv_wr = QEDR_MAX_RQE_ELEMENTS_PER_RQE;
+       uresp.sges_per_srq_wr = dev->attr.max_srq_sge;
+       uresp.max_cqes = QEDR_MAX_CQES;
+
+       rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
+       if (rc)
+               goto err;
+
+       ctx->dev = dev;
+
+       rc = qedr_add_mmap(ctx, ctx->dpi_phys_addr, ctx->dpi_size);
+       if (rc)
+               goto err;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "Allocating user context %p\n",
+                &ctx->ibucontext);
+       return &ctx->ibucontext;
+
+err:
+       kfree(ctx);
+       return ERR_PTR(rc);
+}
+
+int qedr_dealloc_ucontext(struct ib_ucontext *ibctx)
+{
+       struct qedr_ucontext *uctx = get_qedr_ucontext(ibctx);
+       struct qedr_mm *mm, *tmp;
+       int status = 0;
+
+       DP_DEBUG(uctx->dev, QEDR_MSG_INIT, "Deallocating user context %p\n",
+                uctx);
+       uctx->dev->ops->rdma_remove_user(uctx->dev->rdma_ctx, uctx->dpi);
+
+       list_for_each_entry_safe(mm, tmp, &uctx->mm_head, entry) {
+               DP_DEBUG(uctx->dev, QEDR_MSG_MISC,
+                        "deleted (addr=0x%llx,len=0x%lx) for ctx=%p\n",
+                        mm->key.phy_addr, mm->key.len, uctx);
+               list_del(&mm->entry);
+               kfree(mm);
+       }
+
+       kfree(uctx);
+       return status;
+}
+
+int qedr_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
+{
+       struct qedr_ucontext *ucontext = get_qedr_ucontext(context);
+       struct qedr_dev *dev = get_qedr_dev(context->device);
+       unsigned long vm_page = vma->vm_pgoff << PAGE_SHIFT;
+       u64 unmapped_db = dev->db_phys_addr;
+       unsigned long len = (vma->vm_end - vma->vm_start);
+       int rc = 0;
+       bool found;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT,
+                "qedr_mmap called vm_page=0x%lx vm_pgoff=0x%lx unmapped_db=0x%llx db_size=%x, len=%lx\n",
+                vm_page, vma->vm_pgoff, unmapped_db, dev->db_size, len);
+       if (vma->vm_start & (PAGE_SIZE - 1)) {
+               DP_ERR(dev, "Vma_start not page aligned = %ld\n",
+                      vma->vm_start);
+               return -EINVAL;
+       }
+
+       found = qedr_search_mmap(ucontext, vm_page, len);
+       if (!found) {
+               DP_ERR(dev, "Vma_pgoff not found in mapped array = %ld\n",
+                      vma->vm_pgoff);
+               return -EINVAL;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "Mapping doorbell bar\n");
+
+       if ((vm_page >= unmapped_db) && (vm_page <= (unmapped_db +
+                                                    dev->db_size))) {
+               DP_DEBUG(dev, QEDR_MSG_INIT, "Mapping doorbell bar\n");
+               if (vma->vm_flags & VM_READ) {
+                       DP_ERR(dev, "Trying to map doorbell bar for read\n");
+                       return -EPERM;
+               }
+
+               vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+               rc = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                                       PAGE_SIZE, vma->vm_page_prot);
+       } else {
+               DP_DEBUG(dev, QEDR_MSG_INIT, "Mapping chains\n");
+               rc = remap_pfn_range(vma, vma->vm_start,
+                                    vma->vm_pgoff, len, vma->vm_page_prot);
+       }
+       DP_DEBUG(dev, QEDR_MSG_INIT, "qedr_mmap return code: %d\n", rc);
+       return rc;
+}
+
+struct ib_pd *qedr_alloc_pd(struct ib_device *ibdev,
+                           struct ib_ucontext *context, struct ib_udata *udata)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+       struct qedr_ucontext *uctx = NULL;
+       struct qedr_alloc_pd_uresp uresp;
+       struct qedr_pd *pd;
+       u16 pd_id;
+       int rc;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "Function called from: %s\n",
+                (udata && context) ? "User Lib" : "Kernel");
+
+       if (!dev->rdma_ctx) {
+               DP_ERR(dev, "invlaid RDMA context\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+       if (!pd)
+               return ERR_PTR(-ENOMEM);
+
+       dev->ops->rdma_alloc_pd(dev->rdma_ctx, &pd_id);
+
+       uresp.pd_id = pd_id;
+       pd->pd_id = pd_id;
+
+       if (udata && context) {
+               rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
+               if (rc)
+                       DP_ERR(dev, "copy error pd_id=0x%x.\n", pd_id);
+               uctx = get_qedr_ucontext(context);
+               uctx->pd = pd;
+               pd->uctx = uctx;
+       }
+
+       return &pd->ibpd;
+}
+
+int qedr_dealloc_pd(struct ib_pd *ibpd)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibpd->device);
+       struct qedr_pd *pd = get_qedr_pd(ibpd);
+
+       if (!pd)
+               pr_err("Invalid PD received in dealloc_pd\n");
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "Deallocating PD %d\n", pd->pd_id);
+       dev->ops->rdma_dealloc_pd(dev->rdma_ctx, pd->pd_id);
+
+       kfree(pd);
+
+       return 0;
+}
+
+static void qedr_free_pbl(struct qedr_dev *dev,
+                         struct qedr_pbl_info *pbl_info, struct qedr_pbl *pbl)
+{
+       struct pci_dev *pdev = dev->pdev;
+       int i;
+
+       for (i = 0; i < pbl_info->num_pbls; i++) {
+               if (!pbl[i].va)
+                       continue;
+               dma_free_coherent(&pdev->dev, pbl_info->pbl_size,
+                                 pbl[i].va, pbl[i].pa);
+       }
+
+       kfree(pbl);
+}
+
+#define MIN_FW_PBL_PAGE_SIZE (4 * 1024)
+#define MAX_FW_PBL_PAGE_SIZE (64 * 1024)
+
+#define NUM_PBES_ON_PAGE(_page_size) (_page_size / sizeof(u64))
+#define MAX_PBES_ON_PAGE NUM_PBES_ON_PAGE(MAX_FW_PBL_PAGE_SIZE)
+#define MAX_PBES_TWO_LAYER (MAX_PBES_ON_PAGE * MAX_PBES_ON_PAGE)
+
+static struct qedr_pbl *qedr_alloc_pbl_tbl(struct qedr_dev *dev,
+                                          struct qedr_pbl_info *pbl_info,
+                                          gfp_t flags)
+{
+       struct pci_dev *pdev = dev->pdev;
+       struct qedr_pbl *pbl_table;
+       dma_addr_t *pbl_main_tbl;
+       dma_addr_t pa;
+       void *va;
+       int i;
+
+       pbl_table = kcalloc(pbl_info->num_pbls, sizeof(*pbl_table), flags);
+       if (!pbl_table)
+               return ERR_PTR(-ENOMEM);
+
+       for (i = 0; i < pbl_info->num_pbls; i++) {
+               va = dma_alloc_coherent(&pdev->dev, pbl_info->pbl_size,
+                                       &pa, flags);
+               if (!va)
+                       goto err;
+
+               memset(va, 0, pbl_info->pbl_size);
+               pbl_table[i].va = va;
+               pbl_table[i].pa = pa;
+       }
+
+       /* Two-Layer PBLs, if we have more than one pbl we need to initialize
+        * the first one with physical pointers to all of the rest
+        */
+       pbl_main_tbl = (dma_addr_t *)pbl_table[0].va;
+       for (i = 0; i < pbl_info->num_pbls - 1; i++)
+               pbl_main_tbl[i] = pbl_table[i + 1].pa;
+
+       return pbl_table;
+
+err:
+       for (i--; i >= 0; i--)
+               dma_free_coherent(&pdev->dev, pbl_info->pbl_size,
+                                 pbl_table[i].va, pbl_table[i].pa);
+
+       qedr_free_pbl(dev, pbl_info, pbl_table);
+
+       return ERR_PTR(-ENOMEM);
+}
+
+static int qedr_prepare_pbl_tbl(struct qedr_dev *dev,
+                               struct qedr_pbl_info *pbl_info,
+                               u32 num_pbes, int two_layer_capable)
+{
+       u32 pbl_capacity;
+       u32 pbl_size;
+       u32 num_pbls;
+
+       if ((num_pbes > MAX_PBES_ON_PAGE) && two_layer_capable) {
+               if (num_pbes > MAX_PBES_TWO_LAYER) {
+                       DP_ERR(dev, "prepare pbl table: too many pages %d\n",
+                              num_pbes);
+                       return -EINVAL;
+               }
+
+               /* calculate required pbl page size */
+               pbl_size = MIN_FW_PBL_PAGE_SIZE;
+               pbl_capacity = NUM_PBES_ON_PAGE(pbl_size) *
+                              NUM_PBES_ON_PAGE(pbl_size);
+
+               while (pbl_capacity < num_pbes) {
+                       pbl_size *= 2;
+                       pbl_capacity = pbl_size / sizeof(u64);
+                       pbl_capacity = pbl_capacity * pbl_capacity;
+               }
+
+               num_pbls = DIV_ROUND_UP(num_pbes, NUM_PBES_ON_PAGE(pbl_size));
+               num_pbls++;     /* One for the layer0 ( points to the pbls) */
+               pbl_info->two_layered = true;
+       } else {
+               /* One layered PBL */
+               num_pbls = 1;
+               pbl_size = max_t(u32, MIN_FW_PBL_PAGE_SIZE,
+                                roundup_pow_of_two((num_pbes * sizeof(u64))));
+               pbl_info->two_layered = false;
+       }
+
+       pbl_info->num_pbls = num_pbls;
+       pbl_info->pbl_size = pbl_size;
+       pbl_info->num_pbes = num_pbes;
+
+       DP_DEBUG(dev, QEDR_MSG_MR,
+                "prepare pbl table: num_pbes=%d, num_pbls=%d, pbl_size=%d\n",
+                pbl_info->num_pbes, pbl_info->num_pbls, pbl_info->pbl_size);
+
+       return 0;
+}
+
+static void qedr_populate_pbls(struct qedr_dev *dev, struct ib_umem *umem,
+                              struct qedr_pbl *pbl,
+                              struct qedr_pbl_info *pbl_info)
+{
+       int shift, pg_cnt, pages, pbe_cnt, total_num_pbes = 0;
+       struct qedr_pbl *pbl_tbl;
+       struct scatterlist *sg;
+       struct regpair *pbe;
+       int entry;
+       u32 addr;
+
+       if (!pbl_info->num_pbes)
+               return;
+
+       /* If we have a two layered pbl, the first pbl points to the rest
+        * of the pbls and the first entry lays on the second pbl in the table
+        */
+       if (pbl_info->two_layered)
+               pbl_tbl = &pbl[1];
+       else
+               pbl_tbl = pbl;
+
+       pbe = (struct regpair *)pbl_tbl->va;
+       if (!pbe) {
+               DP_ERR(dev, "cannot populate PBL due to a NULL PBE\n");
+               return;
+       }
+
+       pbe_cnt = 0;
+
+       shift = ilog2(umem->page_size);
+
+       for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
+               pages = sg_dma_len(sg) >> shift;
+               for (pg_cnt = 0; pg_cnt < pages; pg_cnt++) {
+                       /* store the page address in pbe */
+                       pbe->lo = cpu_to_le32(sg_dma_address(sg) +
+                                             umem->page_size * pg_cnt);
+                       addr = upper_32_bits(sg_dma_address(sg) +
+                                            umem->page_size * pg_cnt);
+                       pbe->hi = cpu_to_le32(addr);
+                       pbe_cnt++;
+                       total_num_pbes++;
+                       pbe++;
+
+                       if (total_num_pbes == pbl_info->num_pbes)
+                               return;
+
+                       /* If the given pbl is full storing the pbes,
+                        * move to next pbl.
+                        */
+                       if (pbe_cnt == (pbl_info->pbl_size / sizeof(u64))) {
+                               pbl_tbl++;
+                               pbe = (struct regpair *)pbl_tbl->va;
+                               pbe_cnt = 0;
+                       }
+               }
+       }
+}
+
+static int qedr_copy_cq_uresp(struct qedr_dev *dev,
+                             struct qedr_cq *cq, struct ib_udata *udata)
+{
+       struct qedr_create_cq_uresp uresp;
+       int rc;
+
+       memset(&uresp, 0, sizeof(uresp));
+
+       uresp.db_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_UCM_RDMA_CQ_CONS_32BIT);
+       uresp.icid = cq->icid;
+
+       rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
+       if (rc)
+               DP_ERR(dev, "copy error cqid=0x%x.\n", cq->icid);
+
+       return rc;
+}
+
+static void consume_cqe(struct qedr_cq *cq)
+{
+       if (cq->latest_cqe == cq->toggle_cqe)
+               cq->pbl_toggle ^= RDMA_CQE_REQUESTER_TOGGLE_BIT_MASK;
+
+       cq->latest_cqe = qed_chain_consume(&cq->pbl);
+}
+
+static inline int qedr_align_cq_entries(int entries)
+{
+       u64 size, aligned_size;
+
+       /* We allocate an extra entry that we don't report to the FW. */
+       size = (entries + 1) * QEDR_CQE_SIZE;
+       aligned_size = ALIGN(size, PAGE_SIZE);
+
+       return aligned_size / QEDR_CQE_SIZE;
+}
+
+static inline int qedr_init_user_queue(struct ib_ucontext *ib_ctx,
+                                      struct qedr_dev *dev,
+                                      struct qedr_userq *q,
+                                      u64 buf_addr, size_t buf_len,
+                                      int access, int dmasync)
+{
+       int page_cnt;
+       int rc;
+
+       q->buf_addr = buf_addr;
+       q->buf_len = buf_len;
+       q->umem = ib_umem_get(ib_ctx, q->buf_addr, q->buf_len, access, dmasync);
+       if (IS_ERR(q->umem)) {
+               DP_ERR(dev, "create user queue: failed ib_umem_get, got %ld\n",
+                      PTR_ERR(q->umem));
+               return PTR_ERR(q->umem);
+       }
+
+       page_cnt = ib_umem_page_count(q->umem);
+       rc = qedr_prepare_pbl_tbl(dev, &q->pbl_info, page_cnt, 0);
+       if (rc)
+               goto err0;
+
+       q->pbl_tbl = qedr_alloc_pbl_tbl(dev, &q->pbl_info, GFP_KERNEL);
+       if (IS_ERR_OR_NULL(q->pbl_tbl))
+               goto err0;
+
+       qedr_populate_pbls(dev, q->umem, q->pbl_tbl, &q->pbl_info);
+
+       return 0;
+
+err0:
+       ib_umem_release(q->umem);
+
+       return rc;
+}
+
+static inline void qedr_init_cq_params(struct qedr_cq *cq,
+                                      struct qedr_ucontext *ctx,
+                                      struct qedr_dev *dev, int vector,
+                                      int chain_entries, int page_cnt,
+                                      u64 pbl_ptr,
+                                      struct qed_rdma_create_cq_in_params
+                                      *params)
+{
+       memset(params, 0, sizeof(*params));
+       params->cq_handle_hi = upper_32_bits((uintptr_t)cq);
+       params->cq_handle_lo = lower_32_bits((uintptr_t)cq);
+       params->cnq_id = vector;
+       params->cq_size = chain_entries - 1;
+       params->dpi = (ctx) ? ctx->dpi : dev->dpi;
+       params->pbl_num_pages = page_cnt;
+       params->pbl_ptr = pbl_ptr;
+       params->pbl_two_level = 0;
+}
+
+static void doorbell_cq(struct qedr_cq *cq, u32 cons, u8 flags)
+{
+       /* Flush data before signalling doorbell */
+       wmb();
+       cq->db.data.agg_flags = flags;
+       cq->db.data.value = cpu_to_le32(cons);
+       writeq(cq->db.raw, cq->db_addr);
+
+       /* Make sure write would stick */
+       mmiowb();
+}
+
+int qedr_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)
+{
+       struct qedr_cq *cq = get_qedr_cq(ibcq);
+       unsigned long sflags;
+
+       if (cq->cq_type == QEDR_CQ_TYPE_GSI)
+               return 0;
+
+       spin_lock_irqsave(&cq->cq_lock, sflags);
+
+       cq->arm_flags = 0;
+
+       if (flags & IB_CQ_SOLICITED)
+               cq->arm_flags |= DQ_UCM_ROCE_CQ_ARM_SE_CF_CMD;
+
+       if (flags & IB_CQ_NEXT_COMP)
+               cq->arm_flags |= DQ_UCM_ROCE_CQ_ARM_CF_CMD;
+
+       doorbell_cq(cq, cq->cq_cons - 1, cq->arm_flags);
+
+       spin_unlock_irqrestore(&cq->cq_lock, sflags);
+
+       return 0;
+}
+
+struct ib_cq *qedr_create_cq(struct ib_device *ibdev,
+                            const struct ib_cq_init_attr *attr,
+                            struct ib_ucontext *ib_ctx, struct ib_udata *udata)
+{
+       struct qedr_ucontext *ctx = get_qedr_ucontext(ib_ctx);
+       struct qed_rdma_destroy_cq_out_params destroy_oparams;
+       struct qed_rdma_destroy_cq_in_params destroy_iparams;
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+       struct qed_rdma_create_cq_in_params params;
+       struct qedr_create_cq_ureq ureq;
+       int vector = attr->comp_vector;
+       int entries = attr->cqe;
+       struct qedr_cq *cq;
+       int chain_entries;
+       int page_cnt;
+       u64 pbl_ptr;
+       u16 icid;
+       int rc;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT,
+                "create_cq: called from %s. entries=%d, vector=%d\n",
+                udata ? "User Lib" : "Kernel", entries, vector);
+
+       if (entries > QEDR_MAX_CQES) {
+               DP_ERR(dev,
+                      "create cq: the number of entries %d is too high. Must be equal or below %d.\n",
+                      entries, QEDR_MAX_CQES);
+               return ERR_PTR(-EINVAL);
+       }
+
+       chain_entries = qedr_align_cq_entries(entries);
+       chain_entries = min_t(int, chain_entries, QEDR_MAX_CQES);
+
+       cq = kzalloc(sizeof(*cq), GFP_KERNEL);
+       if (!cq)
+               return ERR_PTR(-ENOMEM);
+
+       if (udata) {
+               memset(&ureq, 0, sizeof(ureq));
+               if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) {
+                       DP_ERR(dev,
+                              "create cq: problem copying data from user space\n");
+                       goto err0;
+               }
+
+               if (!ureq.len) {
+                       DP_ERR(dev,
+                              "create cq: cannot create a cq with 0 entries\n");
+                       goto err0;
+               }
+
+               cq->cq_type = QEDR_CQ_TYPE_USER;
+
+               rc = qedr_init_user_queue(ib_ctx, dev, &cq->q, ureq.addr,
+                                         ureq.len, IB_ACCESS_LOCAL_WRITE, 1);
+               if (rc)
+                       goto err0;
+
+               pbl_ptr = cq->q.pbl_tbl->pa;
+               page_cnt = cq->q.pbl_info.num_pbes;
+       } else {
+               cq->cq_type = QEDR_CQ_TYPE_KERNEL;
+
+               rc = dev->ops->common->chain_alloc(dev->cdev,
+                                                  QED_CHAIN_USE_TO_CONSUME,
+                                                  QED_CHAIN_MODE_PBL,
+                                                  QED_CHAIN_CNT_TYPE_U32,
+                                                  chain_entries,
+                                                  sizeof(union rdma_cqe),
+                                                  &cq->pbl);
+               if (rc)
+                       goto err1;
+
+               page_cnt = qed_chain_get_page_cnt(&cq->pbl);
+               pbl_ptr = qed_chain_get_pbl_phys(&cq->pbl);
+       }
+
+       qedr_init_cq_params(cq, ctx, dev, vector, chain_entries, page_cnt,
+                           pbl_ptr, &params);
+
+       rc = dev->ops->rdma_create_cq(dev->rdma_ctx, &params, &icid);
+       if (rc)
+               goto err2;
+
+       cq->icid = icid;
+       cq->sig = QEDR_CQ_MAGIC_NUMBER;
+       spin_lock_init(&cq->cq_lock);
+
+       if (ib_ctx) {
+               rc = qedr_copy_cq_uresp(dev, cq, udata);
+               if (rc)
+                       goto err3;
+       } else {
+               /* Generate doorbell address. */
+               cq->db_addr = dev->db_addr +
+                   DB_ADDR_SHIFT(DQ_PWM_OFFSET_UCM_RDMA_CQ_CONS_32BIT);
+               cq->db.data.icid = cq->icid;
+               cq->db.data.params = DB_AGG_CMD_SET <<
+                   RDMA_PWM_VAL32_DATA_AGG_CMD_SHIFT;
+
+               /* point to the very last element, passing it we will toggle */
+               cq->toggle_cqe = qed_chain_get_last_elem(&cq->pbl);
+               cq->pbl_toggle = RDMA_CQE_REQUESTER_TOGGLE_BIT_MASK;
+               cq->latest_cqe = NULL;
+               consume_cqe(cq);
+               cq->cq_cons = qed_chain_get_cons_idx_u32(&cq->pbl);
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_CQ,
+                "create cq: icid=0x%0x, addr=%p, size(entries)=0x%0x\n",
+                cq->icid, cq, params.cq_size);
+
+       return &cq->ibcq;
+
+err3:
+       destroy_iparams.icid = cq->icid;
+       dev->ops->rdma_destroy_cq(dev->rdma_ctx, &destroy_iparams,
+                                 &destroy_oparams);
+err2:
+       if (udata)
+               qedr_free_pbl(dev, &cq->q.pbl_info, cq->q.pbl_tbl);
+       else
+               dev->ops->common->chain_free(dev->cdev, &cq->pbl);
+err1:
+       if (udata)
+               ib_umem_release(cq->q.umem);
+err0:
+       kfree(cq);
+       return ERR_PTR(-EINVAL);
+}
+
+int qedr_resize_cq(struct ib_cq *ibcq, int new_cnt, struct ib_udata *udata)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibcq->device);
+       struct qedr_cq *cq = get_qedr_cq(ibcq);
+
+       DP_ERR(dev, "cq %p RESIZE NOT SUPPORTED\n", cq);
+
+       return 0;
+}
+
+int qedr_destroy_cq(struct ib_cq *ibcq)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibcq->device);
+       struct qed_rdma_destroy_cq_out_params oparams;
+       struct qed_rdma_destroy_cq_in_params iparams;
+       struct qedr_cq *cq = get_qedr_cq(ibcq);
+
+       DP_DEBUG(dev, QEDR_MSG_CQ, "destroy cq: cq_id %d", cq->icid);
+
+       /* GSIs CQs are handled by driver, so they don't exist in the FW */
+       if (cq->cq_type != QEDR_CQ_TYPE_GSI) {
+               iparams.icid = cq->icid;
+               dev->ops->rdma_destroy_cq(dev->rdma_ctx, &iparams, &oparams);
+               dev->ops->common->chain_free(dev->cdev, &cq->pbl);
+       }
+
+       if (ibcq->uobject && ibcq->uobject->context) {
+               qedr_free_pbl(dev, &cq->q.pbl_info, cq->q.pbl_tbl);
+               ib_umem_release(cq->q.umem);
+       }
+
+       kfree(cq);
+
+       return 0;
+}
+
+static inline int get_gid_info_from_table(struct ib_qp *ibqp,
+                                         struct ib_qp_attr *attr,
+                                         int attr_mask,
+                                         struct qed_rdma_modify_qp_in_params
+                                         *qp_params)
+{
+       enum rdma_network_type nw_type;
+       struct ib_gid_attr gid_attr;
+       union ib_gid gid;
+       u32 ipv4_addr;
+       int rc = 0;
+       int i;
+
+       rc = ib_get_cached_gid(ibqp->device, attr->ah_attr.port_num,
+                              attr->ah_attr.grh.sgid_index, &gid, &gid_attr);
+       if (rc)
+               return rc;
+
+       if (!memcmp(&gid, &zgid, sizeof(gid)))
+               return -ENOENT;
+
+       if (gid_attr.ndev) {
+               qp_params->vlan_id = rdma_vlan_dev_vlan_id(gid_attr.ndev);
+
+               dev_put(gid_attr.ndev);
+               nw_type = ib_gid_to_network_type(gid_attr.gid_type, &gid);
+               switch (nw_type) {
+               case RDMA_NETWORK_IPV6:
+                       memcpy(&qp_params->sgid.bytes[0], &gid.raw[0],
+                              sizeof(qp_params->sgid));
+                       memcpy(&qp_params->dgid.bytes[0],
+                              &attr->ah_attr.grh.dgid,
+                              sizeof(qp_params->dgid));
+                       qp_params->roce_mode = ROCE_V2_IPV6;
+                       SET_FIELD(qp_params->modify_flags,
+                                 QED_ROCE_MODIFY_QP_VALID_ROCE_MODE, 1);
+                       break;
+               case RDMA_NETWORK_IB:
+                       memcpy(&qp_params->sgid.bytes[0], &gid.raw[0],
+                              sizeof(qp_params->sgid));
+                       memcpy(&qp_params->dgid.bytes[0],
+                              &attr->ah_attr.grh.dgid,
+                              sizeof(qp_params->dgid));
+                       qp_params->roce_mode = ROCE_V1;
+                       break;
+               case RDMA_NETWORK_IPV4:
+                       memset(&qp_params->sgid, 0, sizeof(qp_params->sgid));
+                       memset(&qp_params->dgid, 0, sizeof(qp_params->dgid));
+                       ipv4_addr = qedr_get_ipv4_from_gid(gid.raw);
+                       qp_params->sgid.ipv4_addr = ipv4_addr;
+                       ipv4_addr =
+                           qedr_get_ipv4_from_gid(attr->ah_attr.grh.dgid.raw);
+                       qp_params->dgid.ipv4_addr = ipv4_addr;
+                       SET_FIELD(qp_params->modify_flags,
+                                 QED_ROCE_MODIFY_QP_VALID_ROCE_MODE, 1);
+                       qp_params->roce_mode = ROCE_V2_IPV4;
+                       break;
+               }
+       }
+
+       for (i = 0; i < 4; i++) {
+               qp_params->sgid.dwords[i] = ntohl(qp_params->sgid.dwords[i]);
+               qp_params->dgid.dwords[i] = ntohl(qp_params->dgid.dwords[i]);
+       }
+
+       if (qp_params->vlan_id >= VLAN_CFI_MASK)
+               qp_params->vlan_id = 0;
+
+       return 0;
+}
+
+static void qedr_cleanup_user_sq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       qedr_free_pbl(dev, &qp->usq.pbl_info, qp->usq.pbl_tbl);
+       ib_umem_release(qp->usq.umem);
+}
+
+static void qedr_cleanup_user_rq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       qedr_free_pbl(dev, &qp->urq.pbl_info, qp->urq.pbl_tbl);
+       ib_umem_release(qp->urq.umem);
+}
+
+static void qedr_cleanup_kernel_sq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       dev->ops->common->chain_free(dev->cdev, &qp->sq.pbl);
+       kfree(qp->wqe_wr_id);
+}
+
+static void qedr_cleanup_kernel_rq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       dev->ops->common->chain_free(dev->cdev, &qp->rq.pbl);
+       kfree(qp->rqe_wr_id);
+}
+
+static int qedr_check_qp_attrs(struct ib_pd *ibpd, struct qedr_dev *dev,
+                              struct ib_qp_init_attr *attrs)
+{
+       struct qedr_device_attr *qattr = &dev->attr;
+
+       /* QP0... attrs->qp_type == IB_QPT_GSI */
+       if (attrs->qp_type != IB_QPT_RC && attrs->qp_type != IB_QPT_GSI) {
+               DP_DEBUG(dev, QEDR_MSG_QP,
+                        "create qp: unsupported qp type=0x%x requested\n",
+                        attrs->qp_type);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_send_wr > qattr->max_sqe) {
+               DP_ERR(dev,
+                      "create qp: cannot create a SQ with %d elements (max_send_wr=0x%x)\n",
+                      attrs->cap.max_send_wr, qattr->max_sqe);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_inline_data > qattr->max_inline) {
+               DP_ERR(dev,
+                      "create qp: unsupported inline data size=0x%x requested (max_inline=0x%x)\n",
+                      attrs->cap.max_inline_data, qattr->max_inline);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_send_sge > qattr->max_sge) {
+               DP_ERR(dev,
+                      "create qp: unsupported send_sge=0x%x requested (max_send_sge=0x%x)\n",
+                      attrs->cap.max_send_sge, qattr->max_sge);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_recv_sge > qattr->max_sge) {
+               DP_ERR(dev,
+                      "create qp: unsupported recv_sge=0x%x requested (max_recv_sge=0x%x)\n",
+                      attrs->cap.max_recv_sge, qattr->max_sge);
+               return -EINVAL;
+       }
+
+       /* Unprivileged user space cannot create special QP */
+       if (ibpd->uobject && attrs->qp_type == IB_QPT_GSI) {
+               DP_ERR(dev,
+                      "create qp: userspace can't create special QPs of type=0x%x\n",
+                      attrs->qp_type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void qedr_copy_rq_uresp(struct qedr_create_qp_uresp *uresp,
+                              struct qedr_qp *qp)
+{
+       uresp->rq_db_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_ROCE_RQ_PROD);
+       uresp->rq_icid = qp->icid;
+}
+
+static void qedr_copy_sq_uresp(struct qedr_create_qp_uresp *uresp,
+                              struct qedr_qp *qp)
+{
+       uresp->sq_db_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_XCM_RDMA_SQ_PROD);
+       uresp->sq_icid = qp->icid + 1;
+}
+
+static int qedr_copy_qp_uresp(struct qedr_dev *dev,
+                             struct qedr_qp *qp, struct ib_udata *udata)
+{
+       struct qedr_create_qp_uresp uresp;
+       int rc;
+
+       memset(&uresp, 0, sizeof(uresp));
+       qedr_copy_sq_uresp(&uresp, qp);
+       qedr_copy_rq_uresp(&uresp, qp);
+
+       uresp.atomic_supported = dev->atomic_cap != IB_ATOMIC_NONE;
+       uresp.qp_id = qp->qp_id;
+
+       rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
+       if (rc)
+               DP_ERR(dev,
+                      "create qp: failed a copy to user space with qp icid=0x%x.\n",
+                      qp->icid);
+
+       return rc;
+}
+
+static void qedr_set_qp_init_params(struct qedr_dev *dev,
+                                   struct qedr_qp *qp,
+                                   struct qedr_pd *pd,
+                                   struct ib_qp_init_attr *attrs)
+{
+       qp->pd = pd;
+
+       spin_lock_init(&qp->q_lock);
+
+       qp->qp_type = attrs->qp_type;
+       qp->max_inline_data = attrs->cap.max_inline_data;
+       qp->sq.max_sges = attrs->cap.max_send_sge;
+       qp->state = QED_ROCE_QP_STATE_RESET;
+       qp->signaled = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR) ? true : false;
+       qp->sq_cq = get_qedr_cq(attrs->send_cq);
+       qp->rq_cq = get_qedr_cq(attrs->recv_cq);
+       qp->dev = dev;
+
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "QP params:\tpd = %d, qp_type = %d, max_inline_data = %d, state = %d, signaled = %d, use_srq=%d\n",
+                pd->pd_id, qp->qp_type, qp->max_inline_data,
+                qp->state, qp->signaled, (attrs->srq) ? 1 : 0);
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "SQ params:\tsq_max_sges = %d, sq_cq_id = %d\n",
+                qp->sq.max_sges, qp->sq_cq->icid);
+       qp->rq.max_sges = attrs->cap.max_recv_sge;
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "RQ params:\trq_max_sges = %d, rq_cq_id = %d\n",
+                qp->rq.max_sges, qp->rq_cq->icid);
+}
+
+static inline void
+qedr_init_qp_user_params(struct qed_rdma_create_qp_in_params *params,
+                        struct qedr_create_qp_ureq *ureq)
+{
+       /* QP handle to be written in CQE */
+       params->qp_handle_lo = ureq->qp_handle_lo;
+       params->qp_handle_hi = ureq->qp_handle_hi;
+}
+
+static inline void
+qedr_init_qp_kernel_doorbell_sq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       qp->sq.db = dev->db_addr +
+                   DB_ADDR_SHIFT(DQ_PWM_OFFSET_XCM_RDMA_SQ_PROD);
+       qp->sq.db_data.data.icid = qp->icid + 1;
+}
+
+static inline void
+qedr_init_qp_kernel_doorbell_rq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       qp->rq.db = dev->db_addr +
+                   DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_ROCE_RQ_PROD);
+       qp->rq.db_data.data.icid = qp->icid;
+}
+
+static inline int
+qedr_init_qp_kernel_params_rq(struct qedr_dev *dev,
+                             struct qedr_qp *qp, struct ib_qp_init_attr *attrs)
+{
+       /* Allocate driver internal RQ array */
+       qp->rqe_wr_id = kcalloc(qp->rq.max_wr, sizeof(*qp->rqe_wr_id),
+                               GFP_KERNEL);
+       if (!qp->rqe_wr_id)
+               return -ENOMEM;
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "RQ max_wr set to %d.\n", qp->rq.max_wr);
+
+       return 0;
+}
+
+static inline int
+qedr_init_qp_kernel_params_sq(struct qedr_dev *dev,
+                             struct qedr_qp *qp,
+                             struct ib_qp_init_attr *attrs,
+                             struct qed_rdma_create_qp_in_params *params)
+{
+       u32 temp_max_wr;
+
+       /* Allocate driver internal SQ array */
+       temp_max_wr = attrs->cap.max_send_wr * dev->wq_multiplier;
+       temp_max_wr = min_t(u32, temp_max_wr, dev->attr.max_sqe);
+
+       /* temp_max_wr < attr->max_sqe < u16 so the casting is safe */
+       qp->sq.max_wr = (u16)temp_max_wr;
+       qp->wqe_wr_id = kcalloc(qp->sq.max_wr, sizeof(*qp->wqe_wr_id),
+                               GFP_KERNEL);
+       if (!qp->wqe_wr_id)
+               return -ENOMEM;
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "SQ max_wr set to %d.\n", qp->sq.max_wr);
+
+       /* QP handle to be written in CQE */
+       params->qp_handle_lo = lower_32_bits((uintptr_t)qp);
+       params->qp_handle_hi = upper_32_bits((uintptr_t)qp);
+
+       return 0;
+}
+
+static inline int qedr_init_qp_kernel_sq(struct qedr_dev *dev,
+                                        struct qedr_qp *qp,
+                                        struct ib_qp_init_attr *attrs)
+{
+       u32 n_sq_elems, n_sq_entries;
+       int rc;
+
+       /* A single work request may take up to QEDR_MAX_SQ_WQE_SIZE elements in
+        * the ring. The ring should allow at least a single WR, even if the
+        * user requested none, due to allocation issues.
+        */
+       n_sq_entries = attrs->cap.max_send_wr;
+       n_sq_entries = min_t(u32, n_sq_entries, dev->attr.max_sqe);
+       n_sq_entries = max_t(u32, n_sq_entries, 1);
+       n_sq_elems = n_sq_entries * QEDR_MAX_SQE_ELEMENTS_PER_SQE;
+       rc = dev->ops->common->chain_alloc(dev->cdev,
+                                          QED_CHAIN_USE_TO_PRODUCE,
+                                          QED_CHAIN_MODE_PBL,
+                                          QED_CHAIN_CNT_TYPE_U32,
+                                          n_sq_elems,
+                                          QEDR_SQE_ELEMENT_SIZE,
+                                          &qp->sq.pbl);
+       if (rc) {
+               DP_ERR(dev, "failed to allocate QP %p SQ\n", qp);
+               return rc;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_SQ,
+                "SQ Pbl base addr = %llx max_send_wr=%d max_wr=%d capacity=%d, rc=%d\n",
+                qed_chain_get_pbl_phys(&qp->sq.pbl), attrs->cap.max_send_wr,
+                n_sq_entries, qed_chain_get_capacity(&qp->sq.pbl), rc);
+       return 0;
+}
+
+static inline int qedr_init_qp_kernel_rq(struct qedr_dev *dev,
+                                        struct qedr_qp *qp,
+                                        struct ib_qp_init_attr *attrs)
+{
+       u32 n_rq_elems, n_rq_entries;
+       int rc;
+
+       /* A single work request may take up to QEDR_MAX_RQ_WQE_SIZE elements in
+        * the ring. There ring should allow at least a single WR, even if the
+        * user requested none, due to allocation issues.
+        */
+       n_rq_entries = max_t(u32, attrs->cap.max_recv_wr, 1);
+       n_rq_elems = n_rq_entries * QEDR_MAX_RQE_ELEMENTS_PER_RQE;
+       rc = dev->ops->common->chain_alloc(dev->cdev,
+                                          QED_CHAIN_USE_TO_CONSUME_PRODUCE,
+                                          QED_CHAIN_MODE_PBL,
+                                          QED_CHAIN_CNT_TYPE_U32,
+                                          n_rq_elems,
+                                          QEDR_RQE_ELEMENT_SIZE,
+                                          &qp->rq.pbl);
+
+       if (rc) {
+               DP_ERR(dev, "failed to allocate memory for QP %p RQ\n", qp);
+               return -ENOMEM;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_RQ,
+                "RQ Pbl base addr = %llx max_recv_wr=%d max_wr=%d capacity=%d, rc=%d\n",
+                qed_chain_get_pbl_phys(&qp->rq.pbl), attrs->cap.max_recv_wr,
+                n_rq_entries, qed_chain_get_capacity(&qp->rq.pbl), rc);
+
+       /* n_rq_entries < u16 so the casting is safe */
+       qp->rq.max_wr = (u16)n_rq_entries;
+
+       return 0;
+}
+
+static inline void
+qedr_init_qp_in_params_sq(struct qedr_dev *dev,
+                         struct qedr_pd *pd,
+                         struct qedr_qp *qp,
+                         struct ib_qp_init_attr *attrs,
+                         struct ib_udata *udata,
+                         struct qed_rdma_create_qp_in_params *params)
+{
+       /* QP handle to be written in an async event */
+       params->qp_handle_async_lo = lower_32_bits((uintptr_t)qp);
+       params->qp_handle_async_hi = upper_32_bits((uintptr_t)qp);
+
+       params->signal_all = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR);
+       params->fmr_and_reserved_lkey = !udata;
+       params->pd = pd->pd_id;
+       params->dpi = pd->uctx ? pd->uctx->dpi : dev->dpi;
+       params->sq_cq_id = get_qedr_cq(attrs->send_cq)->icid;
+       params->max_sq_sges = 0;
+       params->stats_queue = 0;
+
+       if (udata) {
+               params->sq_num_pages = qp->usq.pbl_info.num_pbes;
+               params->sq_pbl_ptr = qp->usq.pbl_tbl->pa;
+       } else {
+               params->sq_num_pages = qed_chain_get_page_cnt(&qp->sq.pbl);
+               params->sq_pbl_ptr = qed_chain_get_pbl_phys(&qp->sq.pbl);
+       }
+}
+
+static inline void
+qedr_init_qp_in_params_rq(struct qedr_qp *qp,
+                         struct ib_qp_init_attr *attrs,
+                         struct ib_udata *udata,
+                         struct qed_rdma_create_qp_in_params *params)
+{
+       params->rq_cq_id = get_qedr_cq(attrs->recv_cq)->icid;
+       params->srq_id = 0;
+       params->use_srq = false;
+
+       if (udata) {
+               params->rq_num_pages = qp->urq.pbl_info.num_pbes;
+               params->rq_pbl_ptr = qp->urq.pbl_tbl->pa;
+       } else {
+               params->rq_num_pages = qed_chain_get_page_cnt(&qp->rq.pbl);
+               params->rq_pbl_ptr = qed_chain_get_pbl_phys(&qp->rq.pbl);
+       }
+}
+
+static inline void qedr_qp_user_print(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "create qp: successfully created user QP. qp=%p, sq_addr=0x%llx, sq_len=%zd, rq_addr=0x%llx, rq_len=%zd\n",
+                qp, qp->usq.buf_addr, qp->usq.buf_len, qp->urq.buf_addr,
+                qp->urq.buf_len);
+}
+
+static inline int qedr_init_user_qp(struct ib_ucontext *ib_ctx,
+                                   struct qedr_dev *dev,
+                                   struct qedr_qp *qp,
+                                   struct qedr_create_qp_ureq *ureq)
+{
+       int rc;
+
+       /* SQ - read access only (0), dma sync not required (0) */
+       rc = qedr_init_user_queue(ib_ctx, dev, &qp->usq, ureq->sq_addr,
+                                 ureq->sq_len, 0, 0);
+       if (rc)
+               return rc;
+
+       /* RQ - read access only (0), dma sync not required (0) */
+       rc = qedr_init_user_queue(ib_ctx, dev, &qp->urq, ureq->rq_addr,
+                                 ureq->rq_len, 0, 0);
+
+       if (rc)
+               qedr_cleanup_user_sq(dev, qp);
+       return rc;
+}
+
+static inline int
+qedr_init_kernel_qp(struct qedr_dev *dev,
+                   struct qedr_qp *qp,
+                   struct ib_qp_init_attr *attrs,
+                   struct qed_rdma_create_qp_in_params *params)
+{
+       int rc;
+
+       rc = qedr_init_qp_kernel_sq(dev, qp, attrs);
+       if (rc) {
+               DP_ERR(dev, "failed to init kernel QP %p SQ\n", qp);
+               return rc;
+       }
+
+       rc = qedr_init_qp_kernel_params_sq(dev, qp, attrs, params);
+       if (rc) {
+               dev->ops->common->chain_free(dev->cdev, &qp->sq.pbl);
+               DP_ERR(dev, "failed to init kernel QP %p SQ params\n", qp);
+               return rc;
+       }
+
+       rc = qedr_init_qp_kernel_rq(dev, qp, attrs);
+       if (rc) {
+               qedr_cleanup_kernel_sq(dev, qp);
+               DP_ERR(dev, "failed to init kernel QP %p RQ\n", qp);
+               return rc;
+       }
+
+       rc = qedr_init_qp_kernel_params_rq(dev, qp, attrs);
+       if (rc) {
+               DP_ERR(dev, "failed to init kernel QP %p RQ params\n", qp);
+               qedr_cleanup_kernel_sq(dev, qp);
+               dev->ops->common->chain_free(dev->cdev, &qp->rq.pbl);
+               return rc;
+       }
+
+       return rc;
+}
+
+struct ib_qp *qedr_create_qp(struct ib_pd *ibpd,
+                            struct ib_qp_init_attr *attrs,
+                            struct ib_udata *udata)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibpd->device);
+       struct qed_rdma_create_qp_out_params out_params;
+       struct qed_rdma_create_qp_in_params in_params;
+       struct qedr_pd *pd = get_qedr_pd(ibpd);
+       struct ib_ucontext *ib_ctx = NULL;
+       struct qedr_ucontext *ctx = NULL;
+       struct qedr_create_qp_ureq ureq;
+       struct qedr_qp *qp;
+       int rc = 0;
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "create qp: called from %s, pd=%p\n",
+                udata ? "user library" : "kernel", pd);
+
+       rc = qedr_check_qp_attrs(ibpd, dev, attrs);
+       if (rc)
+               return ERR_PTR(rc);
+
+       qp = kzalloc(sizeof(*qp), GFP_KERNEL);
+       if (!qp)
+               return ERR_PTR(-ENOMEM);
+
+       if (attrs->srq)
+               return ERR_PTR(-EINVAL);
+
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "create qp: sq_cq=%p, sq_icid=%d, rq_cq=%p, rq_icid=%d\n",
+                get_qedr_cq(attrs->send_cq),
+                get_qedr_cq(attrs->send_cq)->icid,
+                get_qedr_cq(attrs->recv_cq),
+                get_qedr_cq(attrs->recv_cq)->icid);
+
+       qedr_set_qp_init_params(dev, qp, pd, attrs);
+
+       if (attrs->qp_type == IB_QPT_GSI) {
+               if (udata) {
+                       DP_ERR(dev,
+                              "create qp: unexpected udata when creating GSI QP\n");
+                       goto err0;
+               }
+               return qedr_create_gsi_qp(dev, attrs, qp);
+       }
+
+       memset(&in_params, 0, sizeof(in_params));
+
+       if (udata) {
+               if (!(udata && ibpd->uobject && ibpd->uobject->context))
+                       goto err0;
+
+               ib_ctx = ibpd->uobject->context;
+               ctx = get_qedr_ucontext(ib_ctx);
+
+               memset(&ureq, 0, sizeof(ureq));
+               if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) {
+                       DP_ERR(dev,
+                              "create qp: problem copying data from user space\n");
+                       goto err0;
+               }
+
+               rc = qedr_init_user_qp(ib_ctx, dev, qp, &ureq);
+               if (rc)
+                       goto err0;
+
+               qedr_init_qp_user_params(&in_params, &ureq);
+       } else {
+               rc = qedr_init_kernel_qp(dev, qp, attrs, &in_params);
+               if (rc)
+                       goto err0;
+       }
+
+       qedr_init_qp_in_params_sq(dev, pd, qp, attrs, udata, &in_params);
+       qedr_init_qp_in_params_rq(qp, attrs, udata, &in_params);
+
+       qp->qed_qp = dev->ops->rdma_create_qp(dev->rdma_ctx,
+                                             &in_params, &out_params);
+
+       if (!qp->qed_qp)
+               goto err1;
+
+       qp->qp_id = out_params.qp_id;
+       qp->icid = out_params.icid;
+       qp->ibqp.qp_num = qp->qp_id;
+
+       if (udata) {
+               rc = qedr_copy_qp_uresp(dev, qp, udata);
+               if (rc)
+                       goto err2;
+
+               qedr_qp_user_print(dev, qp);
+       } else {
+               qedr_init_qp_kernel_doorbell_sq(dev, qp);
+               qedr_init_qp_kernel_doorbell_rq(dev, qp);
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "created %s space QP %p\n",
+                udata ? "user" : "kernel", qp);
+
+       return &qp->ibqp;
+
+err2:
+       rc = dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp);
+       if (rc)
+               DP_ERR(dev, "create qp: fatal fault. rc=%d", rc);
+err1:
+       if (udata) {
+               qedr_cleanup_user_sq(dev, qp);
+               qedr_cleanup_user_rq(dev, qp);
+       } else {
+               qedr_cleanup_kernel_sq(dev, qp);
+               qedr_cleanup_kernel_rq(dev, qp);
+       }
+
+err0:
+       kfree(qp);
+
+       return ERR_PTR(-EFAULT);
+}
+
+enum ib_qp_state qedr_get_ibqp_state(enum qed_roce_qp_state qp_state)
+{
+       switch (qp_state) {
+       case QED_ROCE_QP_STATE_RESET:
+               return IB_QPS_RESET;
+       case QED_ROCE_QP_STATE_INIT:
+               return IB_QPS_INIT;
+       case QED_ROCE_QP_STATE_RTR:
+               return IB_QPS_RTR;
+       case QED_ROCE_QP_STATE_RTS:
+               return IB_QPS_RTS;
+       case QED_ROCE_QP_STATE_SQD:
+               return IB_QPS_SQD;
+       case QED_ROCE_QP_STATE_ERR:
+               return IB_QPS_ERR;
+       case QED_ROCE_QP_STATE_SQE:
+               return IB_QPS_SQE;
+       }
+       return IB_QPS_ERR;
+}
+
+enum qed_roce_qp_state qedr_get_state_from_ibqp(enum ib_qp_state qp_state)
+{
+       switch (qp_state) {
+       case IB_QPS_RESET:
+               return QED_ROCE_QP_STATE_RESET;
+       case IB_QPS_INIT:
+               return QED_ROCE_QP_STATE_INIT;
+       case IB_QPS_RTR:
+               return QED_ROCE_QP_STATE_RTR;
+       case IB_QPS_RTS:
+               return QED_ROCE_QP_STATE_RTS;
+       case IB_QPS_SQD:
+               return QED_ROCE_QP_STATE_SQD;
+       case IB_QPS_ERR:
+               return QED_ROCE_QP_STATE_ERR;
+       default:
+               return QED_ROCE_QP_STATE_ERR;
+       }
+}
+
+static void qedr_reset_qp_hwq_info(struct qedr_qp_hwq_info *qph)
+{
+       qed_chain_reset(&qph->pbl);
+       qph->prod = 0;
+       qph->cons = 0;
+       qph->wqe_cons = 0;
+       qph->db_data.data.value = cpu_to_le16(0);
+}
+
+static int qedr_update_qp_state(struct qedr_dev *dev,
+                               struct qedr_qp *qp,
+                               enum qed_roce_qp_state new_state)
+{
+       int status = 0;
+
+       if (new_state == qp->state)
+               return 1;
+
+       switch (qp->state) {
+       case QED_ROCE_QP_STATE_RESET:
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_INIT:
+                       qp->prev_wqe_size = 0;
+                       qedr_reset_qp_hwq_info(&qp->sq);
+                       qedr_reset_qp_hwq_info(&qp->rq);
+                       break;
+               default:
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       case QED_ROCE_QP_STATE_INIT:
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_RTR:
+                       /* Update doorbell (in case post_recv was
+                        * done before move to RTR)
+                        */
+                       wmb();
+                       writel(qp->rq.db_data.raw, qp->rq.db);
+                       /* Make sure write takes effect */
+                       mmiowb();
+                       break;
+               case QED_ROCE_QP_STATE_ERR:
+                       break;
+               default:
+                       /* Invalid state change. */
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       case QED_ROCE_QP_STATE_RTR:
+               /* RTR->XXX */
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_RTS:
+                       break;
+               case QED_ROCE_QP_STATE_ERR:
+                       break;
+               default:
+                       /* Invalid state change. */
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       case QED_ROCE_QP_STATE_RTS:
+               /* RTS->XXX */
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_SQD:
+                       break;
+               case QED_ROCE_QP_STATE_ERR:
+                       break;
+               default:
+                       /* Invalid state change. */
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       case QED_ROCE_QP_STATE_SQD:
+               /* SQD->XXX */
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_RTS:
+               case QED_ROCE_QP_STATE_ERR:
+                       break;
+               default:
+                       /* Invalid state change. */
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       case QED_ROCE_QP_STATE_ERR:
+               /* ERR->XXX */
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_RESET:
+                       break;
+               default:
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       default:
+               status = -EINVAL;
+               break;
+       };
+
+       return status;
+}
+
+int qedr_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+                  int attr_mask, struct ib_udata *udata)
+{
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qed_rdma_modify_qp_in_params qp_params = { 0 };
+       struct qedr_dev *dev = get_qedr_dev(&qp->dev->ibdev);
+       enum ib_qp_state old_qp_state, new_qp_state;
+       int rc = 0;
+
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "modify qp: qp %p attr_mask=0x%x, state=%d", qp, attr_mask,
+                attr->qp_state);
+
+       old_qp_state = qedr_get_ibqp_state(qp->state);
+       if (attr_mask & IB_QP_STATE)
+               new_qp_state = attr->qp_state;
+       else
+               new_qp_state = old_qp_state;
+
+       if (!ib_modify_qp_is_ok
+           (old_qp_state, new_qp_state, ibqp->qp_type, attr_mask,
+            IB_LINK_LAYER_ETHERNET)) {
+               DP_ERR(dev,
+                      "modify qp: invalid attribute mask=0x%x specified for\n"
+                      "qpn=0x%x of type=0x%x old_qp_state=0x%x, new_qp_state=0x%x\n",
+                      attr_mask, qp->qp_id, ibqp->qp_type, old_qp_state,
+                      new_qp_state);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       /* Translate the masks... */
+       if (attr_mask & IB_QP_STATE) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_RDMA_MODIFY_QP_VALID_NEW_STATE, 1);
+               qp_params.new_state = qedr_get_state_from_ibqp(attr->qp_state);
+       }
+
+       if (attr_mask & IB_QP_EN_SQD_ASYNC_NOTIFY)
+               qp_params.sqd_async = true;
+
+       if (attr_mask & IB_QP_PKEY_INDEX) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_PKEY, 1);
+               if (attr->pkey_index >= QEDR_ROCE_PKEY_TABLE_LEN) {
+                       rc = -EINVAL;
+                       goto err;
+               }
+
+               qp_params.pkey = QEDR_ROCE_PKEY_DEFAULT;
+       }
+
+       if (attr_mask & IB_QP_QKEY)
+               qp->qkey = attr->qkey;
+
+       if (attr_mask & IB_QP_ACCESS_FLAGS) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_RDMA_MODIFY_QP_VALID_RDMA_OPS_EN, 1);
+               qp_params.incoming_rdma_read_en = attr->qp_access_flags &
+                                                 IB_ACCESS_REMOTE_READ;
+               qp_params.incoming_rdma_write_en = attr->qp_access_flags &
+                                                  IB_ACCESS_REMOTE_WRITE;
+               qp_params.incoming_atomic_en = attr->qp_access_flags &
+                                              IB_ACCESS_REMOTE_ATOMIC;
+       }
+
+       if (attr_mask & (IB_QP_AV | IB_QP_PATH_MTU)) {
+               if (attr_mask & IB_QP_PATH_MTU) {
+                       if (attr->path_mtu < IB_MTU_256 ||
+                           attr->path_mtu > IB_MTU_4096) {
+                               pr_err("error: Only MTU sizes of 256, 512, 1024, 2048 and 4096 are supported by RoCE\n");
+                               rc = -EINVAL;
+                               goto err;
+                       }
+                       qp->mtu = min(ib_mtu_enum_to_int(attr->path_mtu),
+                                     ib_mtu_enum_to_int(iboe_get_mtu
+                                                        (dev->ndev->mtu)));
+               }
+
+               if (!qp->mtu) {
+                       qp->mtu =
+                       ib_mtu_enum_to_int(iboe_get_mtu(dev->ndev->mtu));
+                       pr_err("Fixing zeroed MTU to qp->mtu = %d\n", qp->mtu);
+               }
+
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_ADDRESS_VECTOR, 1);
+
+               qp_params.traffic_class_tos = attr->ah_attr.grh.traffic_class;
+               qp_params.flow_label = attr->ah_attr.grh.flow_label;
+               qp_params.hop_limit_ttl = attr->ah_attr.grh.hop_limit;
+
+               qp->sgid_idx = attr->ah_attr.grh.sgid_index;
+
+               rc = get_gid_info_from_table(ibqp, attr, attr_mask, &qp_params);
+               if (rc) {
+                       DP_ERR(dev,
+                              "modify qp: problems with GID index %d (rc=%d)\n",
+                              attr->ah_attr.grh.sgid_index, rc);
+                       return rc;
+               }
+
+               rc = qedr_get_dmac(dev, &attr->ah_attr,
+                                  qp_params.remote_mac_addr);
+               if (rc)
+                       return rc;
+
+               qp_params.use_local_mac = true;
+               ether_addr_copy(qp_params.local_mac_addr, dev->ndev->dev_addr);
+
+               DP_DEBUG(dev, QEDR_MSG_QP, "dgid=%x:%x:%x:%x\n",
+                        qp_params.dgid.dwords[0], qp_params.dgid.dwords[1],
+                        qp_params.dgid.dwords[2], qp_params.dgid.dwords[3]);
+               DP_DEBUG(dev, QEDR_MSG_QP, "sgid=%x:%x:%x:%x\n",
+                        qp_params.sgid.dwords[0], qp_params.sgid.dwords[1],
+                        qp_params.sgid.dwords[2], qp_params.sgid.dwords[3]);
+               DP_DEBUG(dev, QEDR_MSG_QP, "remote_mac=[%pM]\n",
+                        qp_params.remote_mac_addr);
+;
+
+               qp_params.mtu = qp->mtu;
+               qp_params.lb_indication = false;
+       }
+
+       if (!qp_params.mtu) {
+               /* Stay with current MTU */
+               if (qp->mtu)
+                       qp_params.mtu = qp->mtu;
+               else
+                       qp_params.mtu =
+                           ib_mtu_enum_to_int(iboe_get_mtu(dev->ndev->mtu));
+       }
+
+       if (attr_mask & IB_QP_TIMEOUT) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_ACK_TIMEOUT, 1);
+
+               qp_params.ack_timeout = attr->timeout;
+               if (attr->timeout) {
+                       u32 temp;
+
+                       temp = 4096 * (1UL << attr->timeout) / 1000 / 1000;
+                       /* FW requires [msec] */
+                       qp_params.ack_timeout = temp;
+               } else {
+                       /* Infinite */
+                       qp_params.ack_timeout = 0;
+               }
+       }
+       if (attr_mask & IB_QP_RETRY_CNT) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_RETRY_CNT, 1);
+               qp_params.retry_cnt = attr->retry_cnt;
+       }
+
+       if (attr_mask & IB_QP_RNR_RETRY) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_RNR_RETRY_CNT, 1);
+               qp_params.rnr_retry_cnt = attr->rnr_retry;
+       }
+
+       if (attr_mask & IB_QP_RQ_PSN) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_RQ_PSN, 1);
+               qp_params.rq_psn = attr->rq_psn;
+               qp->rq_psn = attr->rq_psn;
+       }
+
+       if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) {
+               if (attr->max_rd_atomic > dev->attr.max_qp_req_rd_atomic_resc) {
+                       rc = -EINVAL;
+                       DP_ERR(dev,
+                              "unsupported max_rd_atomic=%d, supported=%d\n",
+                              attr->max_rd_atomic,
+                              dev->attr.max_qp_req_rd_atomic_resc);
+                       goto err;
+               }
+
+               SET_FIELD(qp_params.modify_flags,
+                         QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_REQ, 1);
+               qp_params.max_rd_atomic_req = attr->max_rd_atomic;
+       }
+
+       if (attr_mask & IB_QP_MIN_RNR_TIMER) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_MIN_RNR_NAK_TIMER, 1);
+               qp_params.min_rnr_nak_timer = attr->min_rnr_timer;
+       }
+
+       if (attr_mask & IB_QP_SQ_PSN) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_SQ_PSN, 1);
+               qp_params.sq_psn = attr->sq_psn;
+               qp->sq_psn = attr->sq_psn;
+       }
+
+       if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) {
+               if (attr->max_dest_rd_atomic >
+                   dev->attr.max_qp_resp_rd_atomic_resc) {
+                       DP_ERR(dev,
+                              "unsupported max_dest_rd_atomic=%d, supported=%d\n",
+                              attr->max_dest_rd_atomic,
+                              dev->attr.max_qp_resp_rd_atomic_resc);
+
+                       rc = -EINVAL;
+                       goto err;
+               }
+
+               SET_FIELD(qp_params.modify_flags,
+                         QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_RESP, 1);
+               qp_params.max_rd_atomic_resp = attr->max_dest_rd_atomic;
+       }
+
+       if (attr_mask & IB_QP_DEST_QPN) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_DEST_QP, 1);
+
+               qp_params.dest_qp = attr->dest_qp_num;
+               qp->dest_qp_num = attr->dest_qp_num;
+       }
+
+       if (qp->qp_type != IB_QPT_GSI)
+               rc = dev->ops->rdma_modify_qp(dev->rdma_ctx,
+                                             qp->qed_qp, &qp_params);
+
+       if (attr_mask & IB_QP_STATE) {
+               if ((qp->qp_type != IB_QPT_GSI) && (!udata))
+                       qedr_update_qp_state(dev, qp, qp_params.new_state);
+               qp->state = qp_params.new_state;
+       }
+
+err:
+       return rc;
+}
+
+static int qedr_to_ib_qp_acc_flags(struct qed_rdma_query_qp_out_params *params)
+{
+       int ib_qp_acc_flags = 0;
+
+       if (params->incoming_rdma_write_en)
+               ib_qp_acc_flags |= IB_ACCESS_REMOTE_WRITE;
+       if (params->incoming_rdma_read_en)
+               ib_qp_acc_flags |= IB_ACCESS_REMOTE_READ;
+       if (params->incoming_atomic_en)
+               ib_qp_acc_flags |= IB_ACCESS_REMOTE_ATOMIC;
+       ib_qp_acc_flags |= IB_ACCESS_LOCAL_WRITE;
+       return ib_qp_acc_flags;
+}
+
+int qedr_query_qp(struct ib_qp *ibqp,
+                 struct ib_qp_attr *qp_attr,
+                 int attr_mask, struct ib_qp_init_attr *qp_init_attr)
+{
+       struct qed_rdma_query_qp_out_params params;
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qedr_dev *dev = qp->dev;
+       int rc = 0;
+
+       memset(&params, 0, sizeof(params));
+
+       rc = dev->ops->rdma_query_qp(dev->rdma_ctx, qp->qed_qp, &params);
+       if (rc)
+               goto err;
+
+       memset(qp_attr, 0, sizeof(*qp_attr));
+       memset(qp_init_attr, 0, sizeof(*qp_init_attr));
+
+       qp_attr->qp_state = qedr_get_ibqp_state(params.state);
+       qp_attr->cur_qp_state = qedr_get_ibqp_state(params.state);
+       qp_attr->path_mtu = iboe_get_mtu(params.mtu);
+       qp_attr->path_mig_state = IB_MIG_MIGRATED;
+       qp_attr->rq_psn = params.rq_psn;
+       qp_attr->sq_psn = params.sq_psn;
+       qp_attr->dest_qp_num = params.dest_qp;
+
+       qp_attr->qp_access_flags = qedr_to_ib_qp_acc_flags(&params);
+
+       qp_attr->cap.max_send_wr = qp->sq.max_wr;
+       qp_attr->cap.max_recv_wr = qp->rq.max_wr;
+       qp_attr->cap.max_send_sge = qp->sq.max_sges;
+       qp_attr->cap.max_recv_sge = qp->rq.max_sges;
+       qp_attr->cap.max_inline_data = qp->max_inline_data;
+       qp_init_attr->cap = qp_attr->cap;
+
+       memcpy(&qp_attr->ah_attr.grh.dgid.raw[0], &params.dgid.bytes[0],
+              sizeof(qp_attr->ah_attr.grh.dgid.raw));
+
+       qp_attr->ah_attr.grh.flow_label = params.flow_label;
+       qp_attr->ah_attr.grh.sgid_index = qp->sgid_idx;
+       qp_attr->ah_attr.grh.hop_limit = params.hop_limit_ttl;
+       qp_attr->ah_attr.grh.traffic_class = params.traffic_class_tos;
+
+       qp_attr->ah_attr.ah_flags = IB_AH_GRH;
+       qp_attr->ah_attr.port_num = 1;
+       qp_attr->ah_attr.sl = 0;
+       qp_attr->timeout = params.timeout;
+       qp_attr->rnr_retry = params.rnr_retry;
+       qp_attr->retry_cnt = params.retry_cnt;
+       qp_attr->min_rnr_timer = params.min_rnr_nak_timer;
+       qp_attr->pkey_index = params.pkey_index;
+       qp_attr->port_num = 1;
+       qp_attr->ah_attr.src_path_bits = 0;
+       qp_attr->ah_attr.static_rate = 0;
+       qp_attr->alt_pkey_index = 0;
+       qp_attr->alt_port_num = 0;
+       qp_attr->alt_timeout = 0;
+       memset(&qp_attr->alt_ah_attr, 0, sizeof(qp_attr->alt_ah_attr));
+
+       qp_attr->sq_draining = (params.state == QED_ROCE_QP_STATE_SQD) ? 1 : 0;
+       qp_attr->max_dest_rd_atomic = params.max_dest_rd_atomic;
+       qp_attr->max_rd_atomic = params.max_rd_atomic;
+       qp_attr->en_sqd_async_notify = (params.sqd_async) ? 1 : 0;
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "QEDR_QUERY_QP: max_inline_data=%d\n",
+                qp_attr->cap.max_inline_data);
+
+err:
+       return rc;
+}
+
+int qedr_destroy_qp(struct ib_qp *ibqp)
+{
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qedr_dev *dev = qp->dev;
+       struct ib_qp_attr attr;
+       int attr_mask = 0;
+       int rc = 0;
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "destroy qp: destroying %p, qp type=%d\n",
+                qp, qp->qp_type);
+
+       if (qp->state != (QED_ROCE_QP_STATE_RESET | QED_ROCE_QP_STATE_ERR |
+                         QED_ROCE_QP_STATE_INIT)) {
+               attr.qp_state = IB_QPS_ERR;
+               attr_mask |= IB_QP_STATE;
+
+               /* Change the QP state to ERROR */
+               qedr_modify_qp(ibqp, &attr, attr_mask, NULL);
+       }
+
+       if (qp->qp_type != IB_QPT_GSI) {
+               rc = dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp);
+               if (rc)
+                       return rc;
+       } else {
+               qedr_destroy_gsi_qp(dev);
+       }
+
+       if (ibqp->uobject && ibqp->uobject->context) {
+               qedr_cleanup_user_sq(dev, qp);
+               qedr_cleanup_user_rq(dev, qp);
+       } else {
+               qedr_cleanup_kernel_sq(dev, qp);
+               qedr_cleanup_kernel_rq(dev, qp);
+       }
+
+       kfree(qp);
+
+       return rc;
+}
+
+struct ib_ah *qedr_create_ah(struct ib_pd *ibpd, struct ib_ah_attr *attr)
+{
+       struct qedr_ah *ah;
+
+       ah = kzalloc(sizeof(*ah), GFP_ATOMIC);
+       if (!ah)
+               return ERR_PTR(-ENOMEM);
+
+       ah->attr = *attr;
+
+       return &ah->ibah;
+}
+
+int qedr_destroy_ah(struct ib_ah *ibah)
+{
+       struct qedr_ah *ah = get_qedr_ah(ibah);
+
+       kfree(ah);
+       return 0;
+}
+
+static void free_mr_info(struct qedr_dev *dev, struct mr_info *info)
+{
+       struct qedr_pbl *pbl, *tmp;
+
+       if (info->pbl_table)
+               list_add_tail(&info->pbl_table->list_entry,
+                             &info->free_pbl_list);
+
+       if (!list_empty(&info->inuse_pbl_list))
+               list_splice(&info->inuse_pbl_list, &info->free_pbl_list);
+
+       list_for_each_entry_safe(pbl, tmp, &info->free_pbl_list, list_entry) {
+               list_del(&pbl->list_entry);
+               qedr_free_pbl(dev, &info->pbl_info, pbl);
+       }
+}
+
+static int init_mr_info(struct qedr_dev *dev, struct mr_info *info,
+                       size_t page_list_len, bool two_layered)
+{
+       struct qedr_pbl *tmp;
+       int rc;
+
+       INIT_LIST_HEAD(&info->free_pbl_list);
+       INIT_LIST_HEAD(&info->inuse_pbl_list);
+
+       rc = qedr_prepare_pbl_tbl(dev, &info->pbl_info,
+                                 page_list_len, two_layered);
+       if (rc)
+               goto done;
+
+       info->pbl_table = qedr_alloc_pbl_tbl(dev, &info->pbl_info, GFP_KERNEL);
+       if (!info->pbl_table) {
+               rc = -ENOMEM;
+               goto done;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "pbl_table_pa = %pa\n",
+                &info->pbl_table->pa);
+
+       /* in usual case we use 2 PBLs, so we add one to free
+        * list and allocating another one
+        */
+       tmp = qedr_alloc_pbl_tbl(dev, &info->pbl_info, GFP_KERNEL);
+       if (!tmp) {
+               DP_DEBUG(dev, QEDR_MSG_MR, "Extra PBL is not allocated\n");
+               goto done;
+       }
+
+       list_add_tail(&tmp->list_entry, &info->free_pbl_list);
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "extra pbl_table_pa = %pa\n", &tmp->pa);
+
+done:
+       if (rc)
+               free_mr_info(dev, info);
+
+       return rc;
+}
+
+struct ib_mr *qedr_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len,
+                              u64 usr_addr, int acc, struct ib_udata *udata)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibpd->device);
+       struct qedr_mr *mr;
+       struct qedr_pd *pd;
+       int rc = -ENOMEM;
+
+       pd = get_qedr_pd(ibpd);
+       DP_DEBUG(dev, QEDR_MSG_MR,
+                "qedr_register user mr pd = %d start = %lld, len = %lld, usr_addr = %lld, acc = %d\n",
+                pd->pd_id, start, len, usr_addr, acc);
+
+       if (acc & IB_ACCESS_REMOTE_WRITE && !(acc & IB_ACCESS_LOCAL_WRITE))
+               return ERR_PTR(-EINVAL);
+
+       mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+       if (!mr)
+               return ERR_PTR(rc);
+
+       mr->type = QEDR_MR_USER;
+
+       mr->umem = ib_umem_get(ibpd->uobject->context, start, len, acc, 0);
+       if (IS_ERR(mr->umem)) {
+               rc = -EFAULT;
+               goto err0;
+       }
+
+       rc = init_mr_info(dev, &mr->info, ib_umem_page_count(mr->umem), 1);
+       if (rc)
+               goto err1;
+
+       qedr_populate_pbls(dev, mr->umem, mr->info.pbl_table,
+                          &mr->info.pbl_info);
+
+       rc = dev->ops->rdma_alloc_tid(dev->rdma_ctx, &mr->hw_mr.itid);
+       if (rc) {
+               DP_ERR(dev, "roce alloc tid returned an error %d\n", rc);
+               goto err1;
+       }
+
+       /* Index only, 18 bit long, lkey = itid << 8 | key */
+       mr->hw_mr.tid_type = QED_RDMA_TID_REGISTERED_MR;
+       mr->hw_mr.key = 0;
+       mr->hw_mr.pd = pd->pd_id;
+       mr->hw_mr.local_read = 1;
+       mr->hw_mr.local_write = (acc & IB_ACCESS_LOCAL_WRITE) ? 1 : 0;
+       mr->hw_mr.remote_read = (acc & IB_ACCESS_REMOTE_READ) ? 1 : 0;
+       mr->hw_mr.remote_write = (acc & IB_ACCESS_REMOTE_WRITE) ? 1 : 0;
+       mr->hw_mr.remote_atomic = (acc & IB_ACCESS_REMOTE_ATOMIC) ? 1 : 0;
+       mr->hw_mr.mw_bind = false;
+       mr->hw_mr.pbl_ptr = mr->info.pbl_table[0].pa;
+       mr->hw_mr.pbl_two_level = mr->info.pbl_info.two_layered;
+       mr->hw_mr.pbl_page_size_log = ilog2(mr->info.pbl_info.pbl_size);
+       mr->hw_mr.page_size_log = ilog2(mr->umem->page_size);
+       mr->hw_mr.fbo = ib_umem_offset(mr->umem);
+       mr->hw_mr.length = len;
+       mr->hw_mr.vaddr = usr_addr;
+       mr->hw_mr.zbva = false;
+       mr->hw_mr.phy_mr = false;
+       mr->hw_mr.dma_mr = false;
+
+       rc = dev->ops->rdma_register_tid(dev->rdma_ctx, &mr->hw_mr);
+       if (rc) {
+               DP_ERR(dev, "roce register tid returned an error %d\n", rc);
+               goto err2;
+       }
+
+       mr->ibmr.lkey = mr->hw_mr.itid << 8 | mr->hw_mr.key;
+       if (mr->hw_mr.remote_write || mr->hw_mr.remote_read ||
+           mr->hw_mr.remote_atomic)
+               mr->ibmr.rkey = mr->hw_mr.itid << 8 | mr->hw_mr.key;
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "register user mr lkey: %x\n",
+                mr->ibmr.lkey);
+       return &mr->ibmr;
+
+err2:
+       dev->ops->rdma_free_tid(dev->rdma_ctx, mr->hw_mr.itid);
+err1:
+       qedr_free_pbl(dev, &mr->info.pbl_info, mr->info.pbl_table);
+err0:
+       kfree(mr);
+       return ERR_PTR(rc);
+}
+
+int qedr_dereg_mr(struct ib_mr *ib_mr)
+{
+       struct qedr_mr *mr = get_qedr_mr(ib_mr);
+       struct qedr_dev *dev = get_qedr_dev(ib_mr->device);
+       int rc = 0;
+
+       rc = dev->ops->rdma_deregister_tid(dev->rdma_ctx, mr->hw_mr.itid);
+       if (rc)
+               return rc;
+
+       dev->ops->rdma_free_tid(dev->rdma_ctx, mr->hw_mr.itid);
+
+       if ((mr->type != QEDR_MR_DMA) && (mr->type != QEDR_MR_FRMR))
+               qedr_free_pbl(dev, &mr->info.pbl_info, mr->info.pbl_table);
+
+       /* it could be user registered memory. */
+       if (mr->umem)
+               ib_umem_release(mr->umem);
+
+       kfree(mr);
+
+       return rc;
+}
+
+struct qedr_mr *__qedr_alloc_mr(struct ib_pd *ibpd, int max_page_list_len)
+{
+       struct qedr_pd *pd = get_qedr_pd(ibpd);
+       struct qedr_dev *dev = get_qedr_dev(ibpd->device);
+       struct qedr_mr *mr;
+       int rc = -ENOMEM;
+
+       DP_DEBUG(dev, QEDR_MSG_MR,
+                "qedr_alloc_frmr pd = %d max_page_list_len= %d\n", pd->pd_id,
+                max_page_list_len);
+
+       mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+       if (!mr)
+               return ERR_PTR(rc);
+
+       mr->dev = dev;
+       mr->type = QEDR_MR_FRMR;
+
+       rc = init_mr_info(dev, &mr->info, max_page_list_len, 1);
+       if (rc)
+               goto err0;
+
+       rc = dev->ops->rdma_alloc_tid(dev->rdma_ctx, &mr->hw_mr.itid);
+       if (rc) {
+               DP_ERR(dev, "roce alloc tid returned an error %d\n", rc);
+               goto err0;
+       }
+
+       /* Index only, 18 bit long, lkey = itid << 8 | key */
+       mr->hw_mr.tid_type = QED_RDMA_TID_FMR;
+       mr->hw_mr.key = 0;
+       mr->hw_mr.pd = pd->pd_id;
+       mr->hw_mr.local_read = 1;
+       mr->hw_mr.local_write = 0;
+       mr->hw_mr.remote_read = 0;
+       mr->hw_mr.remote_write = 0;
+       mr->hw_mr.remote_atomic = 0;
+       mr->hw_mr.mw_bind = false;
+       mr->hw_mr.pbl_ptr = 0;
+       mr->hw_mr.pbl_two_level = mr->info.pbl_info.two_layered;
+       mr->hw_mr.pbl_page_size_log = ilog2(mr->info.pbl_info.pbl_size);
+       mr->hw_mr.fbo = 0;
+       mr->hw_mr.length = 0;
+       mr->hw_mr.vaddr = 0;
+       mr->hw_mr.zbva = false;
+       mr->hw_mr.phy_mr = true;
+       mr->hw_mr.dma_mr = false;
+
+       rc = dev->ops->rdma_register_tid(dev->rdma_ctx, &mr->hw_mr);
+       if (rc) {
+               DP_ERR(dev, "roce register tid returned an error %d\n", rc);
+               goto err1;
+       }
+
+       mr->ibmr.lkey = mr->hw_mr.itid << 8 | mr->hw_mr.key;
+       mr->ibmr.rkey = mr->ibmr.lkey;
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "alloc frmr: %x\n", mr->ibmr.lkey);
+       return mr;
+
+err1:
+       dev->ops->rdma_free_tid(dev->rdma_ctx, mr->hw_mr.itid);
+err0:
+       kfree(mr);
+       return ERR_PTR(rc);
+}
+
+struct ib_mr *qedr_alloc_mr(struct ib_pd *ibpd,
+                           enum ib_mr_type mr_type, u32 max_num_sg)
+{
+       struct qedr_dev *dev;
+       struct qedr_mr *mr;
+
+       if (mr_type != IB_MR_TYPE_MEM_REG)
+               return ERR_PTR(-EINVAL);
+
+       mr = __qedr_alloc_mr(ibpd, max_num_sg);
+
+       if (IS_ERR(mr))
+               return ERR_PTR(-EINVAL);
+
+       dev = mr->dev;
+
+       return &mr->ibmr;
+}
+
+static int qedr_set_page(struct ib_mr *ibmr, u64 addr)
+{
+       struct qedr_mr *mr = get_qedr_mr(ibmr);
+       struct qedr_pbl *pbl_table;
+       struct regpair *pbe;
+       u32 pbes_in_page;
+
+       if (unlikely(mr->npages == mr->info.pbl_info.num_pbes)) {
+               DP_ERR(mr->dev, "qedr_set_page failes when %d\n", mr->npages);
+               return -ENOMEM;
+       }
+
+       DP_DEBUG(mr->dev, QEDR_MSG_MR, "qedr_set_page pages[%d] = 0x%llx\n",
+                mr->npages, addr);
+
+       pbes_in_page = mr->info.pbl_info.pbl_size / sizeof(u64);
+       pbl_table = mr->info.pbl_table + (mr->npages / pbes_in_page);
+       pbe = (struct regpair *)pbl_table->va;
+       pbe +=  mr->npages % pbes_in_page;
+       pbe->lo = cpu_to_le32((u32)addr);
+       pbe->hi = cpu_to_le32((u32)upper_32_bits(addr));
+
+       mr->npages++;
+
+       return 0;
+}
+
+static void handle_completed_mrs(struct qedr_dev *dev, struct mr_info *info)
+{
+       int work = info->completed - info->completed_handled - 1;
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "Special FMR work = %d\n", work);
+       while (work-- > 0 && !list_empty(&info->inuse_pbl_list)) {
+               struct qedr_pbl *pbl;
+
+               /* Free all the page list that are possible to be freed
+                * (all the ones that were invalidated), under the assumption
+                * that if an FMR was completed successfully that means that
+                * if there was an invalidate operation before it also ended
+                */
+               pbl = list_first_entry(&info->inuse_pbl_list,
+                                      struct qedr_pbl, list_entry);
+               list_del(&pbl->list_entry);
+               list_add_tail(&pbl->list_entry, &info->free_pbl_list);
+               info->completed_handled++;
+       }
+}
+
+int qedr_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg,
+                  int sg_nents, unsigned int *sg_offset)
+{
+       struct qedr_mr *mr = get_qedr_mr(ibmr);
+
+       mr->npages = 0;
+
+       handle_completed_mrs(mr->dev, &mr->info);
+       return ib_sg_to_pages(ibmr, sg, sg_nents, NULL, qedr_set_page);
+}
+
+struct ib_mr *qedr_get_dma_mr(struct ib_pd *ibpd, int acc)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibpd->device);
+       struct qedr_pd *pd = get_qedr_pd(ibpd);
+       struct qedr_mr *mr;
+       int rc;
+
+       mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+       if (!mr)
+               return ERR_PTR(-ENOMEM);
+
+       mr->type = QEDR_MR_DMA;
+
+       rc = dev->ops->rdma_alloc_tid(dev->rdma_ctx, &mr->hw_mr.itid);
+       if (rc) {
+               DP_ERR(dev, "roce alloc tid returned an error %d\n", rc);
+               goto err1;
+       }
+
+       /* index only, 18 bit long, lkey = itid << 8 | key */
+       mr->hw_mr.tid_type = QED_RDMA_TID_REGISTERED_MR;
+       mr->hw_mr.pd = pd->pd_id;
+       mr->hw_mr.local_read = 1;
+       mr->hw_mr.local_write = (acc & IB_ACCESS_LOCAL_WRITE) ? 1 : 0;
+       mr->hw_mr.remote_read = (acc & IB_ACCESS_REMOTE_READ) ? 1 : 0;
+       mr->hw_mr.remote_write = (acc & IB_ACCESS_REMOTE_WRITE) ? 1 : 0;
+       mr->hw_mr.remote_atomic = (acc & IB_ACCESS_REMOTE_ATOMIC) ? 1 : 0;
+       mr->hw_mr.dma_mr = true;
+
+       rc = dev->ops->rdma_register_tid(dev->rdma_ctx, &mr->hw_mr);
+       if (rc) {
+               DP_ERR(dev, "roce register tid returned an error %d\n", rc);
+               goto err2;
+       }
+
+       mr->ibmr.lkey = mr->hw_mr.itid << 8 | mr->hw_mr.key;
+       if (mr->hw_mr.remote_write || mr->hw_mr.remote_read ||
+           mr->hw_mr.remote_atomic)
+               mr->ibmr.rkey = mr->hw_mr.itid << 8 | mr->hw_mr.key;
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "get dma mr: lkey = %x\n", mr->ibmr.lkey);
+       return &mr->ibmr;
+
+err2:
+       dev->ops->rdma_free_tid(dev->rdma_ctx, mr->hw_mr.itid);
+err1:
+       kfree(mr);
+       return ERR_PTR(rc);
+}
+
+static inline int qedr_wq_is_full(struct qedr_qp_hwq_info *wq)
+{
+       return (((wq->prod + 1) % wq->max_wr) == wq->cons);
+}
+
+static int sge_data_len(struct ib_sge *sg_list, int num_sge)
+{
+       int i, len = 0;
+
+       for (i = 0; i < num_sge; i++)
+               len += sg_list[i].length;
+
+       return len;
+}
+
+static void swap_wqe_data64(u64 *p)
+{
+       int i;
+
+       for (i = 0; i < QEDR_SQE_ELEMENT_SIZE / sizeof(u64); i++, p++)
+               *p = cpu_to_be64(cpu_to_le64(*p));
+}
+
+static u32 qedr_prepare_sq_inline_data(struct qedr_dev *dev,
+                                      struct qedr_qp *qp, u8 *wqe_size,
+                                      struct ib_send_wr *wr,
+                                      struct ib_send_wr **bad_wr, u8 *bits,
+                                      u8 bit)
+{
+       u32 data_size = sge_data_len(wr->sg_list, wr->num_sge);
+       char *seg_prt, *wqe;
+       int i, seg_siz;
+
+       if (data_size > ROCE_REQ_MAX_INLINE_DATA_SIZE) {
+               DP_ERR(dev, "Too much inline data in WR: %d\n", data_size);
+               *bad_wr = wr;
+               return 0;
+       }
+
+       if (!data_size)
+               return data_size;
+
+       *bits |= bit;
+
+       seg_prt = NULL;
+       wqe = NULL;
+       seg_siz = 0;
+
+       /* Copy data inline */
+       for (i = 0; i < wr->num_sge; i++) {
+               u32 len = wr->sg_list[i].length;
+               void *src = (void *)(uintptr_t)wr->sg_list[i].addr;
+
+               while (len > 0) {
+                       u32 cur;
+
+                       /* New segment required */
+                       if (!seg_siz) {
+                               wqe = (char *)qed_chain_produce(&qp->sq.pbl);
+                               seg_prt = wqe;
+                               seg_siz = sizeof(struct rdma_sq_common_wqe);
+                               (*wqe_size)++;
+                       }
+
+                       /* Calculate currently allowed length */
+                       cur = min_t(u32, len, seg_siz);
+                       memcpy(seg_prt, src, cur);
+
+                       /* Update segment variables */
+                       seg_prt += cur;
+                       seg_siz -= cur;
+
+                       /* Update sge variables */
+                       src += cur;
+                       len -= cur;
+
+                       /* Swap fully-completed segments */
+                       if (!seg_siz)
+                               swap_wqe_data64((u64 *)wqe);
+               }
+       }
+
+       /* swap last not completed segment */
+       if (seg_siz)
+               swap_wqe_data64((u64 *)wqe);
+
+       return data_size;
+}
+
+#define RQ_SGE_SET(sge, vaddr, vlength, vflags)                        \
+       do {                                                    \
+               DMA_REGPAIR_LE(sge->addr, vaddr);               \
+               (sge)->length = cpu_to_le32(vlength);           \
+               (sge)->flags = cpu_to_le32(vflags);             \
+       } while (0)
+
+#define SRQ_HDR_SET(hdr, vwr_id, num_sge)                      \
+       do {                                                    \
+               DMA_REGPAIR_LE(hdr->wr_id, vwr_id);             \
+               (hdr)->num_sges = num_sge;                      \
+       } while (0)
+
+#define SRQ_SGE_SET(sge, vaddr, vlength, vlkey)                        \
+       do {                                                    \
+               DMA_REGPAIR_LE(sge->addr, vaddr);               \
+               (sge)->length = cpu_to_le32(vlength);           \
+               (sge)->l_key = cpu_to_le32(vlkey);              \
+       } while (0)
+
+static u32 qedr_prepare_sq_sges(struct qedr_qp *qp, u8 *wqe_size,
+                               struct ib_send_wr *wr)
+{
+       u32 data_size = 0;
+       int i;
+
+       for (i = 0; i < wr->num_sge; i++) {
+               struct rdma_sq_sge *sge = qed_chain_produce(&qp->sq.pbl);
+
+               DMA_REGPAIR_LE(sge->addr, wr->sg_list[i].addr);
+               sge->l_key = cpu_to_le32(wr->sg_list[i].lkey);
+               sge->length = cpu_to_le32(wr->sg_list[i].length);
+               data_size += wr->sg_list[i].length;
+       }
+
+       if (wqe_size)
+               *wqe_size += wr->num_sge;
+
+       return data_size;
+}
+
+static u32 qedr_prepare_sq_rdma_data(struct qedr_dev *dev,
+                                    struct qedr_qp *qp,
+                                    struct rdma_sq_rdma_wqe_1st *rwqe,
+                                    struct rdma_sq_rdma_wqe_2nd *rwqe2,
+                                    struct ib_send_wr *wr,
+                                    struct ib_send_wr **bad_wr)
+{
+       rwqe2->r_key = cpu_to_le32(rdma_wr(wr)->rkey);
+       DMA_REGPAIR_LE(rwqe2->remote_va, rdma_wr(wr)->remote_addr);
+
+       if (wr->send_flags & IB_SEND_INLINE) {
+               u8 flags = 0;
+
+               SET_FIELD2(flags, RDMA_SQ_RDMA_WQE_1ST_INLINE_FLG, 1);
+               return qedr_prepare_sq_inline_data(dev, qp, &rwqe->wqe_size, wr,
+                                                  bad_wr, &rwqe->flags, flags);
+       }
+
+       return qedr_prepare_sq_sges(qp, &rwqe->wqe_size, wr);
+}
+
+static u32 qedr_prepare_sq_send_data(struct qedr_dev *dev,
+                                    struct qedr_qp *qp,
+                                    struct rdma_sq_send_wqe_1st *swqe,
+                                    struct rdma_sq_send_wqe_2st *swqe2,
+                                    struct ib_send_wr *wr,
+                                    struct ib_send_wr **bad_wr)
+{
+       memset(swqe2, 0, sizeof(*swqe2));
+       if (wr->send_flags & IB_SEND_INLINE) {
+               u8 flags = 0;
+
+               SET_FIELD2(flags, RDMA_SQ_SEND_WQE_INLINE_FLG, 1);
+               return qedr_prepare_sq_inline_data(dev, qp, &swqe->wqe_size, wr,
+                                                  bad_wr, &swqe->flags, flags);
+       }
+
+       return qedr_prepare_sq_sges(qp, &swqe->wqe_size, wr);
+}
+
+static int qedr_prepare_reg(struct qedr_qp *qp,
+                           struct rdma_sq_fmr_wqe_1st *fwqe1,
+                           struct ib_reg_wr *wr)
+{
+       struct qedr_mr *mr = get_qedr_mr(wr->mr);
+       struct rdma_sq_fmr_wqe_2nd *fwqe2;
+
+       fwqe2 = (struct rdma_sq_fmr_wqe_2nd *)qed_chain_produce(&qp->sq.pbl);
+       fwqe1->addr.hi = upper_32_bits(mr->ibmr.iova);
+       fwqe1->addr.lo = lower_32_bits(mr->ibmr.iova);
+       fwqe1->l_key = wr->key;
+
+       SET_FIELD2(fwqe2->access_ctrl, RDMA_SQ_FMR_WQE_2ND_REMOTE_READ,
+                  !!(wr->access & IB_ACCESS_REMOTE_READ));
+       SET_FIELD2(fwqe2->access_ctrl, RDMA_SQ_FMR_WQE_2ND_REMOTE_WRITE,
+                  !!(wr->access & IB_ACCESS_REMOTE_WRITE));
+       SET_FIELD2(fwqe2->access_ctrl, RDMA_SQ_FMR_WQE_2ND_ENABLE_ATOMIC,
+                  !!(wr->access & IB_ACCESS_REMOTE_ATOMIC));
+       SET_FIELD2(fwqe2->access_ctrl, RDMA_SQ_FMR_WQE_2ND_LOCAL_READ, 1);
+       SET_FIELD2(fwqe2->access_ctrl, RDMA_SQ_FMR_WQE_2ND_LOCAL_WRITE,
+                  !!(wr->access & IB_ACCESS_LOCAL_WRITE));
+       fwqe2->fmr_ctrl = 0;
+
+       SET_FIELD2(fwqe2->fmr_ctrl, RDMA_SQ_FMR_WQE_2ND_PAGE_SIZE_LOG,
+                  ilog2(mr->ibmr.page_size) - 12);
+
+       fwqe2->length_hi = 0;
+       fwqe2->length_lo = mr->ibmr.length;
+       fwqe2->pbl_addr.hi = upper_32_bits(mr->info.pbl_table->pa);
+       fwqe2->pbl_addr.lo = lower_32_bits(mr->info.pbl_table->pa);
+
+       qp->wqe_wr_id[qp->sq.prod].mr = mr;
+
+       return 0;
+}
+
+enum ib_wc_opcode qedr_ib_to_wc_opcode(enum ib_wr_opcode opcode)
+{
+       switch (opcode) {
+       case IB_WR_RDMA_WRITE:
+       case IB_WR_RDMA_WRITE_WITH_IMM:
+               return IB_WC_RDMA_WRITE;
+       case IB_WR_SEND_WITH_IMM:
+       case IB_WR_SEND:
+       case IB_WR_SEND_WITH_INV:
+               return IB_WC_SEND;
+       case IB_WR_RDMA_READ:
+               return IB_WC_RDMA_READ;
+       case IB_WR_ATOMIC_CMP_AND_SWP:
+               return IB_WC_COMP_SWAP;
+       case IB_WR_ATOMIC_FETCH_AND_ADD:
+               return IB_WC_FETCH_ADD;
+       case IB_WR_REG_MR:
+               return IB_WC_REG_MR;
+       case IB_WR_LOCAL_INV:
+               return IB_WC_LOCAL_INV;
+       default:
+               return IB_WC_SEND;
+       }
+}
+
+inline bool qedr_can_post_send(struct qedr_qp *qp, struct ib_send_wr *wr)
+{
+       int wq_is_full, err_wr, pbl_is_full;
+       struct qedr_dev *dev = qp->dev;
+
+       /* prevent SQ overflow and/or processing of a bad WR */
+       err_wr = wr->num_sge > qp->sq.max_sges;
+       wq_is_full = qedr_wq_is_full(&qp->sq);
+       pbl_is_full = qed_chain_get_elem_left_u32(&qp->sq.pbl) <
+                     QEDR_MAX_SQE_ELEMENTS_PER_SQE;
+       if (wq_is_full || err_wr || pbl_is_full) {
+               if (wq_is_full && !(qp->err_bitmap & QEDR_QP_ERR_SQ_FULL)) {
+                       DP_ERR(dev,
+                              "error: WQ is full. Post send on QP %p failed (this error appears only once)\n",
+                              qp);
+                       qp->err_bitmap |= QEDR_QP_ERR_SQ_FULL;
+               }
+
+               if (err_wr && !(qp->err_bitmap & QEDR_QP_ERR_BAD_SR)) {
+                       DP_ERR(dev,
+                              "error: WR is bad. Post send on QP %p failed (this error appears only once)\n",
+                              qp);
+                       qp->err_bitmap |= QEDR_QP_ERR_BAD_SR;
+               }
+
+               if (pbl_is_full &&
+                   !(qp->err_bitmap & QEDR_QP_ERR_SQ_PBL_FULL)) {
+                       DP_ERR(dev,
+                              "error: WQ PBL is full. Post send on QP %p failed (this error appears only once)\n",
+                              qp);
+                       qp->err_bitmap |= QEDR_QP_ERR_SQ_PBL_FULL;
+               }
+               return false;
+       }
+       return true;
+}
+
+int __qedr_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                    struct ib_send_wr **bad_wr)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibqp->device);
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct rdma_sq_atomic_wqe_1st *awqe1;
+       struct rdma_sq_atomic_wqe_2nd *awqe2;
+       struct rdma_sq_atomic_wqe_3rd *awqe3;
+       struct rdma_sq_send_wqe_2st *swqe2;
+       struct rdma_sq_local_inv_wqe *iwqe;
+       struct rdma_sq_rdma_wqe_2nd *rwqe2;
+       struct rdma_sq_send_wqe_1st *swqe;
+       struct rdma_sq_rdma_wqe_1st *rwqe;
+       struct rdma_sq_fmr_wqe_1st *fwqe1;
+       struct rdma_sq_common_wqe *wqe;
+       u32 length;
+       int rc = 0;
+       bool comp;
+
+       if (!qedr_can_post_send(qp, wr)) {
+               *bad_wr = wr;
+               return -ENOMEM;
+       }
+
+       wqe = qed_chain_produce(&qp->sq.pbl);
+       qp->wqe_wr_id[qp->sq.prod].signaled =
+               !!(wr->send_flags & IB_SEND_SIGNALED) || qp->signaled;
+
+       wqe->flags = 0;
+       SET_FIELD2(wqe->flags, RDMA_SQ_SEND_WQE_SE_FLG,
+                  !!(wr->send_flags & IB_SEND_SOLICITED));
+       comp = (!!(wr->send_flags & IB_SEND_SIGNALED)) || qp->signaled;
+       SET_FIELD2(wqe->flags, RDMA_SQ_SEND_WQE_COMP_FLG, comp);
+       SET_FIELD2(wqe->flags, RDMA_SQ_SEND_WQE_RD_FENCE_FLG,
+                  !!(wr->send_flags & IB_SEND_FENCE));
+       wqe->prev_wqe_size = qp->prev_wqe_size;
+
+       qp->wqe_wr_id[qp->sq.prod].opcode = qedr_ib_to_wc_opcode(wr->opcode);
+
+       switch (wr->opcode) {
+       case IB_WR_SEND_WITH_IMM:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_SEND_WITH_IMM;
+               swqe = (struct rdma_sq_send_wqe_1st *)wqe;
+               swqe->wqe_size = 2;
+               swqe2 = qed_chain_produce(&qp->sq.pbl);
+
+               swqe->inv_key_or_imm_data = cpu_to_le32(wr->ex.imm_data);
+               length = qedr_prepare_sq_send_data(dev, qp, swqe, swqe2,
+                                                  wr, bad_wr);
+               swqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = swqe->wqe_size;
+               qp->prev_wqe_size = swqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = swqe->length;
+               break;
+       case IB_WR_SEND:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_SEND;
+               swqe = (struct rdma_sq_send_wqe_1st *)wqe;
+
+               swqe->wqe_size = 2;
+               swqe2 = qed_chain_produce(&qp->sq.pbl);
+               length = qedr_prepare_sq_send_data(dev, qp, swqe, swqe2,
+                                                  wr, bad_wr);
+               swqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = swqe->wqe_size;
+               qp->prev_wqe_size = swqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = swqe->length;
+               break;
+       case IB_WR_SEND_WITH_INV:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_SEND_WITH_INVALIDATE;
+               swqe = (struct rdma_sq_send_wqe_1st *)wqe;
+               swqe2 = qed_chain_produce(&qp->sq.pbl);
+               swqe->wqe_size = 2;
+               swqe->inv_key_or_imm_data = cpu_to_le32(wr->ex.invalidate_rkey);
+               length = qedr_prepare_sq_send_data(dev, qp, swqe, swqe2,
+                                                  wr, bad_wr);
+               swqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = swqe->wqe_size;
+               qp->prev_wqe_size = swqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = swqe->length;
+               break;
+
+       case IB_WR_RDMA_WRITE_WITH_IMM:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_RDMA_WR_WITH_IMM;
+               rwqe = (struct rdma_sq_rdma_wqe_1st *)wqe;
+
+               rwqe->wqe_size = 2;
+               rwqe->imm_data = htonl(cpu_to_le32(wr->ex.imm_data));
+               rwqe2 = qed_chain_produce(&qp->sq.pbl);
+               length = qedr_prepare_sq_rdma_data(dev, qp, rwqe, rwqe2,
+                                                  wr, bad_wr);
+               rwqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = rwqe->wqe_size;
+               qp->prev_wqe_size = rwqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = rwqe->length;
+               break;
+       case IB_WR_RDMA_WRITE:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_RDMA_WR;
+               rwqe = (struct rdma_sq_rdma_wqe_1st *)wqe;
+
+               rwqe->wqe_size = 2;
+               rwqe2 = qed_chain_produce(&qp->sq.pbl);
+               length = qedr_prepare_sq_rdma_data(dev, qp, rwqe, rwqe2,
+                                                  wr, bad_wr);
+               rwqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = rwqe->wqe_size;
+               qp->prev_wqe_size = rwqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = rwqe->length;
+               break;
+       case IB_WR_RDMA_READ_WITH_INV:
+               DP_ERR(dev,
+                      "RDMA READ WITH INVALIDATE not supported\n");
+               *bad_wr = wr;
+               rc = -EINVAL;
+               break;
+
+       case IB_WR_RDMA_READ:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_RDMA_RD;
+               rwqe = (struct rdma_sq_rdma_wqe_1st *)wqe;
+
+               rwqe->wqe_size = 2;
+               rwqe2 = qed_chain_produce(&qp->sq.pbl);
+               length = qedr_prepare_sq_rdma_data(dev, qp, rwqe, rwqe2,
+                                                  wr, bad_wr);
+               rwqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = rwqe->wqe_size;
+               qp->prev_wqe_size = rwqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = rwqe->length;
+               break;
+
+       case IB_WR_ATOMIC_CMP_AND_SWP:
+       case IB_WR_ATOMIC_FETCH_AND_ADD:
+               awqe1 = (struct rdma_sq_atomic_wqe_1st *)wqe;
+               awqe1->wqe_size = 4;
+
+               awqe2 = qed_chain_produce(&qp->sq.pbl);
+               DMA_REGPAIR_LE(awqe2->remote_va, atomic_wr(wr)->remote_addr);
+               awqe2->r_key = cpu_to_le32(atomic_wr(wr)->rkey);
+
+               awqe3 = qed_chain_produce(&qp->sq.pbl);
+
+               if (wr->opcode == IB_WR_ATOMIC_FETCH_AND_ADD) {
+                       wqe->req_type = RDMA_SQ_REQ_TYPE_ATOMIC_ADD;
+                       DMA_REGPAIR_LE(awqe3->swap_data,
+                                      atomic_wr(wr)->compare_add);
+               } else {
+                       wqe->req_type = RDMA_SQ_REQ_TYPE_ATOMIC_CMP_AND_SWAP;
+                       DMA_REGPAIR_LE(awqe3->swap_data,
+                                      atomic_wr(wr)->swap);
+                       DMA_REGPAIR_LE(awqe3->cmp_data,
+                                      atomic_wr(wr)->compare_add);
+               }
+
+               qedr_prepare_sq_sges(qp, NULL, wr);
+
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = awqe1->wqe_size;
+               qp->prev_wqe_size = awqe1->wqe_size;
+               break;
+
+       case IB_WR_LOCAL_INV:
+               iwqe = (struct rdma_sq_local_inv_wqe *)wqe;
+               iwqe->wqe_size = 1;
+
+               iwqe->req_type = RDMA_SQ_REQ_TYPE_LOCAL_INVALIDATE;
+               iwqe->inv_l_key = wr->ex.invalidate_rkey;
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = iwqe->wqe_size;
+               qp->prev_wqe_size = iwqe->wqe_size;
+               break;
+       case IB_WR_REG_MR:
+               DP_DEBUG(dev, QEDR_MSG_CQ, "REG_MR\n");
+               wqe->req_type = RDMA_SQ_REQ_TYPE_FAST_MR;
+               fwqe1 = (struct rdma_sq_fmr_wqe_1st *)wqe;
+               fwqe1->wqe_size = 2;
+
+               rc = qedr_prepare_reg(qp, fwqe1, reg_wr(wr));
+               if (rc) {
+                       DP_ERR(dev, "IB_REG_MR failed rc=%d\n", rc);
+                       *bad_wr = wr;
+                       break;
+               }
+
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = fwqe1->wqe_size;
+               qp->prev_wqe_size = fwqe1->wqe_size;
+               break;
+       default:
+               DP_ERR(dev, "invalid opcode 0x%x!\n", wr->opcode);
+               rc = -EINVAL;
+               *bad_wr = wr;
+               break;
+       }
+
+       if (*bad_wr) {
+               u16 value;
+
+               /* Restore prod to its position before
+                * this WR was processed
+                */
+               value = le16_to_cpu(qp->sq.db_data.data.value);
+               qed_chain_set_prod(&qp->sq.pbl, value, wqe);
+
+               /* Restore prev_wqe_size */
+               qp->prev_wqe_size = wqe->prev_wqe_size;
+               rc = -EINVAL;
+               DP_ERR(dev, "POST SEND FAILED\n");
+       }
+
+       return rc;
+}
+
+int qedr_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                  struct ib_send_wr **bad_wr)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibqp->device);
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       unsigned long flags;
+       int rc = 0;
+
+       *bad_wr = NULL;
+
+       if (qp->qp_type == IB_QPT_GSI)
+               return qedr_gsi_post_send(ibqp, wr, bad_wr);
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+
+       if ((qp->state == QED_ROCE_QP_STATE_RESET) ||
+           (qp->state == QED_ROCE_QP_STATE_ERR)) {
+               spin_unlock_irqrestore(&qp->q_lock, flags);
+               *bad_wr = wr;
+               DP_DEBUG(dev, QEDR_MSG_CQ,
+                        "QP in wrong state! QP icid=0x%x state %d\n",
+                        qp->icid, qp->state);
+               return -EINVAL;
+       }
+
+       if (!wr) {
+               DP_ERR(dev, "Got an empty post send.\n");
+               return -EINVAL;
+       }
+
+       while (wr) {
+               rc = __qedr_post_send(ibqp, wr, bad_wr);
+               if (rc)
+                       break;
+
+               qp->wqe_wr_id[qp->sq.prod].wr_id = wr->wr_id;
+
+               qedr_inc_sw_prod(&qp->sq);
+
+               qp->sq.db_data.data.value++;
+
+               wr = wr->next;
+       }
+
+       /* Trigger doorbell
+        * If there was a failure in the first WR then it will be triggered in
+        * vane. However this is not harmful (as long as the producer value is
+        * unchanged). For performance reasons we avoid checking for this
+        * redundant doorbell.
+        */
+       wmb();
+       writel(qp->sq.db_data.raw, qp->sq.db);
+
+       /* Make sure write sticks */
+       mmiowb();
+
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       return rc;
+}
+
+int qedr_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+                  struct ib_recv_wr **bad_wr)
+{
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qedr_dev *dev = qp->dev;
+       unsigned long flags;
+       int status = 0;
+
+       if (qp->qp_type == IB_QPT_GSI)
+               return qedr_gsi_post_recv(ibqp, wr, bad_wr);
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+
+       if ((qp->state == QED_ROCE_QP_STATE_RESET) ||
+           (qp->state == QED_ROCE_QP_STATE_ERR)) {
+               spin_unlock_irqrestore(&qp->q_lock, flags);
+               *bad_wr = wr;
+               return -EINVAL;
+       }
+
+       while (wr) {
+               int i;
+
+               if (qed_chain_get_elem_left_u32(&qp->rq.pbl) <
+                   QEDR_MAX_RQE_ELEMENTS_PER_RQE ||
+                   wr->num_sge > qp->rq.max_sges) {
+                       DP_ERR(dev, "Can't post WR  (%d < %d) || (%d > %d)\n",
+                              qed_chain_get_elem_left_u32(&qp->rq.pbl),
+                              QEDR_MAX_RQE_ELEMENTS_PER_RQE, wr->num_sge,
+                              qp->rq.max_sges);
+                       status = -ENOMEM;
+                       *bad_wr = wr;
+                       break;
+               }
+               for (i = 0; i < wr->num_sge; i++) {
+                       u32 flags = 0;
+                       struct rdma_rq_sge *rqe =
+                           qed_chain_produce(&qp->rq.pbl);
+
+                       /* First one must include the number
+                        * of SGE in the list
+                        */
+                       if (!i)
+                               SET_FIELD(flags, RDMA_RQ_SGE_NUM_SGES,
+                                         wr->num_sge);
+
+                       SET_FIELD(flags, RDMA_RQ_SGE_L_KEY,
+                                 wr->sg_list[i].lkey);
+
+                       RQ_SGE_SET(rqe, wr->sg_list[i].addr,
+                                  wr->sg_list[i].length, flags);
+               }
+
+               /* Special case of no sges. FW requires between 1-4 sges...
+                * in this case we need to post 1 sge with length zero. this is
+                * because rdma write with immediate consumes an RQ.
+                */
+               if (!wr->num_sge) {
+                       u32 flags = 0;
+                       struct rdma_rq_sge *rqe =
+                           qed_chain_produce(&qp->rq.pbl);
+
+                       /* First one must include the number
+                        * of SGE in the list
+                        */
+                       SET_FIELD(flags, RDMA_RQ_SGE_L_KEY, 0);
+                       SET_FIELD(flags, RDMA_RQ_SGE_NUM_SGES, 1);
+
+                       RQ_SGE_SET(rqe, 0, 0, flags);
+                       i = 1;
+               }
+
+               qp->rqe_wr_id[qp->rq.prod].wr_id = wr->wr_id;
+               qp->rqe_wr_id[qp->rq.prod].wqe_size = i;
+
+               qedr_inc_sw_prod(&qp->rq);
+
+               /* Flush all the writes before signalling doorbell */
+               wmb();
+
+               qp->rq.db_data.data.value++;
+
+               writel(qp->rq.db_data.raw, qp->rq.db);
+
+               /* Make sure write sticks */
+               mmiowb();
+
+               wr = wr->next;
+       }
+
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       return status;
+}
+
+static int is_valid_cqe(struct qedr_cq *cq, union rdma_cqe *cqe)
+{
+       struct rdma_cqe_requester *resp_cqe = &cqe->req;
+
+       return (resp_cqe->flags & RDMA_CQE_REQUESTER_TOGGLE_BIT_MASK) ==
+               cq->pbl_toggle;
+}
+
+static struct qedr_qp *cqe_get_qp(union rdma_cqe *cqe)
+{
+       struct rdma_cqe_requester *resp_cqe = &cqe->req;
+       struct qedr_qp *qp;
+
+       qp = (struct qedr_qp *)(uintptr_t)HILO_GEN(resp_cqe->qp_handle.hi,
+                                                  resp_cqe->qp_handle.lo,
+                                                  u64);
+       return qp;
+}
+
+static enum rdma_cqe_type cqe_get_type(union rdma_cqe *cqe)
+{
+       struct rdma_cqe_requester *resp_cqe = &cqe->req;
+
+       return GET_FIELD(resp_cqe->flags, RDMA_CQE_REQUESTER_TYPE);
+}
+
+/* Return latest CQE (needs processing) */
+static union rdma_cqe *get_cqe(struct qedr_cq *cq)
+{
+       return cq->latest_cqe;
+}
+
+/* In fmr we need to increase the number of fmr completed counter for the fmr
+ * algorithm determining whether we can free a pbl or not.
+ * we need to perform this whether the work request was signaled or not. for
+ * this purpose we call this function from the condition that checks if a wr
+ * should be skipped, to make sure we don't miss it ( possibly this fmr
+ * operation was not signalted)
+ */
+static inline void qedr_chk_if_fmr(struct qedr_qp *qp)
+{
+       if (qp->wqe_wr_id[qp->sq.cons].opcode == IB_WC_REG_MR)
+               qp->wqe_wr_id[qp->sq.cons].mr->info.completed++;
+}
+
+static int process_req(struct qedr_dev *dev, struct qedr_qp *qp,
+                      struct qedr_cq *cq, int num_entries,
+                      struct ib_wc *wc, u16 hw_cons, enum ib_wc_status status,
+                      int force)
+{
+       u16 cnt = 0;
+
+       while (num_entries && qp->sq.wqe_cons != hw_cons) {
+               if (!qp->wqe_wr_id[qp->sq.cons].signaled && !force) {
+                       qedr_chk_if_fmr(qp);
+                       /* skip WC */
+                       goto next_cqe;
+               }
+
+               /* fill WC */
+               wc->status = status;
+               wc->wc_flags = 0;
+               wc->src_qp = qp->id;
+               wc->qp = &qp->ibqp;
+
+               wc->wr_id = qp->wqe_wr_id[qp->sq.cons].wr_id;
+               wc->opcode = qp->wqe_wr_id[qp->sq.cons].opcode;
+
+               switch (wc->opcode) {
+               case IB_WC_RDMA_WRITE:
+                       wc->byte_len = qp->wqe_wr_id[qp->sq.cons].bytes_len;
+                       break;
+               case IB_WC_COMP_SWAP:
+               case IB_WC_FETCH_ADD:
+                       wc->byte_len = 8;
+                       break;
+               case IB_WC_REG_MR:
+                       qp->wqe_wr_id[qp->sq.cons].mr->info.completed++;
+                       break;
+               default:
+                       break;
+               }
+
+               num_entries--;
+               wc++;
+               cnt++;
+next_cqe:
+               while (qp->wqe_wr_id[qp->sq.cons].wqe_size--)
+                       qed_chain_consume(&qp->sq.pbl);
+               qedr_inc_sw_cons(&qp->sq);
+       }
+
+       return cnt;
+}
+
+static int qedr_poll_cq_req(struct qedr_dev *dev,
+                           struct qedr_qp *qp, struct qedr_cq *cq,
+                           int num_entries, struct ib_wc *wc,
+                           struct rdma_cqe_requester *req)
+{
+       int cnt = 0;
+
+       switch (req->status) {
+       case RDMA_CQE_REQ_STS_OK:
+               cnt = process_req(dev, qp, cq, num_entries, wc, req->sq_cons,
+                                 IB_WC_SUCCESS, 0);
+               break;
+       case RDMA_CQE_REQ_STS_WORK_REQUEST_FLUSHED_ERR:
+               DP_ERR(dev,
+                      "Error: POLL CQ with RDMA_CQE_REQ_STS_WORK_REQUEST_FLUSHED_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                      cq->icid, qp->icid);
+               cnt = process_req(dev, qp, cq, num_entries, wc, req->sq_cons,
+                                 IB_WC_WR_FLUSH_ERR, 0);
+               break;
+       default:
+               /* process all WQE before the cosumer */
+               qp->state = QED_ROCE_QP_STATE_ERR;
+               cnt = process_req(dev, qp, cq, num_entries, wc,
+                                 req->sq_cons - 1, IB_WC_SUCCESS, 0);
+               wc += cnt;
+               /* if we have extra WC fill it with actual error info */
+               if (cnt < num_entries) {
+                       enum ib_wc_status wc_status;
+
+                       switch (req->status) {
+                       case RDMA_CQE_REQ_STS_BAD_RESPONSE_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_BAD_RESPONSE_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_BAD_RESP_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_LOCAL_LENGTH_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_LOCAL_LENGTH_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_LOC_LEN_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_LOCAL_QP_OPERATION_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_LOCAL_QP_OPERATION_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_LOC_QP_OP_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_LOCAL_PROTECTION_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_LOCAL_PROTECTION_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_LOC_PROT_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_MEMORY_MGT_OPERATION_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_MEMORY_MGT_OPERATION_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_MW_BIND_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_REMOTE_INVALID_REQUEST_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_REMOTE_INVALID_REQUEST_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_REM_INV_REQ_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_REMOTE_ACCESS_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_REMOTE_ACCESS_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_REM_ACCESS_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_REMOTE_OPERATION_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_REMOTE_OPERATION_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_REM_OP_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_RNR_NAK_RETRY_CNT_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_RNR_NAK_RETRY_CNT_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_RNR_RETRY_EXC_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_TRANSPORT_RETRY_CNT_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with ROCE_CQE_REQ_STS_TRANSPORT_RETRY_CNT_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_RETRY_EXC_ERR;
+                               break;
+                       default:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with IB_WC_GENERAL_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_GENERAL_ERR;
+                       }
+                       cnt += process_req(dev, qp, cq, 1, wc, req->sq_cons,
+                                          wc_status, 1);
+               }
+       }
+
+       return cnt;
+}
+
+static void __process_resp_one(struct qedr_dev *dev, struct qedr_qp *qp,
+                              struct qedr_cq *cq, struct ib_wc *wc,
+                              struct rdma_cqe_responder *resp, u64 wr_id)
+{
+       enum ib_wc_status wc_status = IB_WC_SUCCESS;
+       u8 flags;
+
+       wc->opcode = IB_WC_RECV;
+       wc->wc_flags = 0;
+
+       switch (resp->status) {
+       case RDMA_CQE_RESP_STS_LOCAL_ACCESS_ERR:
+               wc_status = IB_WC_LOC_ACCESS_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_LOCAL_LENGTH_ERR:
+               wc_status = IB_WC_LOC_LEN_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_LOCAL_QP_OPERATION_ERR:
+               wc_status = IB_WC_LOC_QP_OP_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_LOCAL_PROTECTION_ERR:
+               wc_status = IB_WC_LOC_PROT_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_MEMORY_MGT_OPERATION_ERR:
+               wc_status = IB_WC_MW_BIND_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_REMOTE_INVALID_REQUEST_ERR:
+               wc_status = IB_WC_REM_INV_RD_REQ_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_OK:
+               wc_status = IB_WC_SUCCESS;
+               wc->byte_len = le32_to_cpu(resp->length);
+
+               flags = resp->flags & QEDR_RESP_RDMA_IMM;
+
+               if (flags == QEDR_RESP_RDMA_IMM)
+                       wc->opcode = IB_WC_RECV_RDMA_WITH_IMM;
+
+               if (flags == QEDR_RESP_RDMA_IMM || flags == QEDR_RESP_IMM) {
+                       wc->ex.imm_data =
+                               le32_to_cpu(resp->imm_data_or_inv_r_Key);
+                       wc->wc_flags |= IB_WC_WITH_IMM;
+               }
+               break;
+       default:
+               wc->status = IB_WC_GENERAL_ERR;
+               DP_ERR(dev, "Invalid CQE status detected\n");
+       }
+
+       /* fill WC */
+       wc->status = wc_status;
+       wc->src_qp = qp->id;
+       wc->qp = &qp->ibqp;
+       wc->wr_id = wr_id;
+}
+
+static int process_resp_one(struct qedr_dev *dev, struct qedr_qp *qp,
+                           struct qedr_cq *cq, struct ib_wc *wc,
+                           struct rdma_cqe_responder *resp)
+{
+       u64 wr_id = qp->rqe_wr_id[qp->rq.cons].wr_id;
+
+       __process_resp_one(dev, qp, cq, wc, resp, wr_id);
+
+       while (qp->rqe_wr_id[qp->rq.cons].wqe_size--)
+               qed_chain_consume(&qp->rq.pbl);
+       qedr_inc_sw_cons(&qp->rq);
+
+       return 1;
+}
+
+static int process_resp_flush(struct qedr_qp *qp, struct qedr_cq *cq,
+                             int num_entries, struct ib_wc *wc, u16 hw_cons)
+{
+       u16 cnt = 0;
+
+       while (num_entries && qp->rq.wqe_cons != hw_cons) {
+               /* fill WC */
+               wc->status = IB_WC_WR_FLUSH_ERR;
+               wc->wc_flags = 0;
+               wc->src_qp = qp->id;
+               wc->byte_len = 0;
+               wc->wr_id = qp->rqe_wr_id[qp->rq.cons].wr_id;
+               wc->qp = &qp->ibqp;
+               num_entries--;
+               wc++;
+               cnt++;
+               while (qp->rqe_wr_id[qp->rq.cons].wqe_size--)
+                       qed_chain_consume(&qp->rq.pbl);
+               qedr_inc_sw_cons(&qp->rq);
+       }
+
+       return cnt;
+}
+
+static void try_consume_resp_cqe(struct qedr_cq *cq, struct qedr_qp *qp,
+                                struct rdma_cqe_responder *resp, int *update)
+{
+       if (le16_to_cpu(resp->rq_cons) == qp->rq.wqe_cons) {
+               consume_cqe(cq);
+               *update |= 1;
+       }
+}
+
+static int qedr_poll_cq_resp(struct qedr_dev *dev, struct qedr_qp *qp,
+                            struct qedr_cq *cq, int num_entries,
+                            struct ib_wc *wc, struct rdma_cqe_responder *resp,
+                            int *update)
+{
+       int cnt;
+
+       if (resp->status == RDMA_CQE_RESP_STS_WORK_REQUEST_FLUSHED_ERR) {
+               cnt = process_resp_flush(qp, cq, num_entries, wc,
+                                        resp->rq_cons);
+               try_consume_resp_cqe(cq, qp, resp, update);
+       } else {
+               cnt = process_resp_one(dev, qp, cq, wc, resp);
+               consume_cqe(cq);
+               *update |= 1;
+       }
+
+       return cnt;
+}
+
+static void try_consume_req_cqe(struct qedr_cq *cq, struct qedr_qp *qp,
+                               struct rdma_cqe_requester *req, int *update)
+{
+       if (le16_to_cpu(req->sq_cons) == qp->sq.wqe_cons) {
+               consume_cqe(cq);
+               *update |= 1;
+       }
+}
+
+int qedr_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibcq->device);
+       struct qedr_cq *cq = get_qedr_cq(ibcq);
+       union rdma_cqe *cqe = cq->latest_cqe;
+       u32 old_cons, new_cons;
+       unsigned long flags;
+       int update = 0;
+       int done = 0;
+
+       if (cq->cq_type == QEDR_CQ_TYPE_GSI)
+               return qedr_gsi_poll_cq(ibcq, num_entries, wc);
+
+       spin_lock_irqsave(&cq->cq_lock, flags);
+       old_cons = qed_chain_get_cons_idx_u32(&cq->pbl);
+       while (num_entries && is_valid_cqe(cq, cqe)) {
+               struct qedr_qp *qp;
+               int cnt = 0;
+
+               /* prevent speculative reads of any field of CQE */
+               rmb();
+
+               qp = cqe_get_qp(cqe);
+               if (!qp) {
+                       WARN(1, "Error: CQE QP pointer is NULL. CQE=%p\n", cqe);
+                       break;
+               }
+
+               wc->qp = &qp->ibqp;
+
+               switch (cqe_get_type(cqe)) {
+               case RDMA_CQE_TYPE_REQUESTER:
+                       cnt = qedr_poll_cq_req(dev, qp, cq, num_entries, wc,
+                                              &cqe->req);
+                       try_consume_req_cqe(cq, qp, &cqe->req, &update);
+                       break;
+               case RDMA_CQE_TYPE_RESPONDER_RQ:
+                       cnt = qedr_poll_cq_resp(dev, qp, cq, num_entries, wc,
+                                               &cqe->resp, &update);
+                       break;
+               case RDMA_CQE_TYPE_INVALID:
+               default:
+                       DP_ERR(dev, "Error: invalid CQE type = %d\n",
+                              cqe_get_type(cqe));
+               }
+               num_entries -= cnt;
+               wc += cnt;
+               done += cnt;
+
+               cqe = get_cqe(cq);
+       }
+       new_cons = qed_chain_get_cons_idx_u32(&cq->pbl);
+
+       cq->cq_cons += new_cons - old_cons;
+
+       if (update)
+               /* doorbell notifies abount latest VALID entry,
+                * but chain already point to the next INVALID one
+                */
+               doorbell_cq(cq, cq->cq_cons - 1, cq->arm_flags);
+
+       spin_unlock_irqrestore(&cq->cq_lock, flags);
+       return done;
+}
+
+int qedr_process_mad(struct ib_device *ibdev, int process_mad_flags,
+                    u8 port_num,
+                    const struct ib_wc *in_wc,
+                    const struct ib_grh *in_grh,
+                    const struct ib_mad_hdr *mad_hdr,
+                    size_t in_mad_size, struct ib_mad_hdr *out_mad,
+                    size_t *out_mad_size, u16 *out_mad_pkey_index)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+
+       DP_DEBUG(dev, QEDR_MSG_GSI,
+                "QEDR_PROCESS_MAD in_mad %x %x %x %x %x %x %x %x\n",
+                mad_hdr->attr_id, mad_hdr->base_version, mad_hdr->attr_mod,
+                mad_hdr->class_specific, mad_hdr->class_version,
+                mad_hdr->method, mad_hdr->mgmt_class, mad_hdr->status);
+       return IB_MAD_RESULT_SUCCESS;
+}
+
+int qedr_port_immutable(struct ib_device *ibdev, u8 port_num,
+                       struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = qedr_query_port(ibdev, port_num, &attr);
+       if (err)
+               return err;
+
+       immutable->pkey_tbl_len = attr.pkey_tbl_len;
+       immutable->gid_tbl_len = attr.gid_tbl_len;
+       immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE |
+                                   RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP;
+       immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+       return 0;
+}
diff --git a/drivers/infiniband/hw/qedr/verbs.h b/drivers/infiniband/hw/qedr/verbs.h
new file mode 100644 (file)
index 0000000..a9b5e67
--- /dev/null
@@ -0,0 +1,101 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed 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, or the
+ * OpenIB.org BSD license below:
+ *
+ *     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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __QEDR_VERBS_H__
+#define __QEDR_VERBS_H__
+
+int qedr_query_device(struct ib_device *ibdev,
+                     struct ib_device_attr *attr, struct ib_udata *udata);
+int qedr_query_port(struct ib_device *, u8 port, struct ib_port_attr *props);
+int qedr_modify_port(struct ib_device *, u8 port, int mask,
+                    struct ib_port_modify *props);
+
+int qedr_query_gid(struct ib_device *, u8 port, int index, union ib_gid *gid);
+
+int qedr_query_pkey(struct ib_device *, u8 port, u16 index, u16 *pkey);
+
+struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *, struct ib_udata *);
+int qedr_dealloc_ucontext(struct ib_ucontext *);
+
+int qedr_mmap(struct ib_ucontext *, struct vm_area_struct *vma);
+int qedr_del_gid(struct ib_device *device, u8 port_num,
+                unsigned int index, void **context);
+int qedr_add_gid(struct ib_device *device, u8 port_num,
+                unsigned int index, const union ib_gid *gid,
+                const struct ib_gid_attr *attr, void **context);
+struct ib_pd *qedr_alloc_pd(struct ib_device *,
+                           struct ib_ucontext *, struct ib_udata *);
+int qedr_dealloc_pd(struct ib_pd *pd);
+
+struct ib_cq *qedr_create_cq(struct ib_device *ibdev,
+                            const struct ib_cq_init_attr *attr,
+                            struct ib_ucontext *ib_ctx,
+                            struct ib_udata *udata);
+int qedr_resize_cq(struct ib_cq *, int cqe, struct ib_udata *);
+int qedr_destroy_cq(struct ib_cq *);
+int qedr_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags);
+struct ib_qp *qedr_create_qp(struct ib_pd *, struct ib_qp_init_attr *attrs,
+                            struct ib_udata *);
+int qedr_modify_qp(struct ib_qp *, struct ib_qp_attr *attr,
+                  int attr_mask, struct ib_udata *udata);
+int qedr_query_qp(struct ib_qp *, struct ib_qp_attr *qp_attr,
+                 int qp_attr_mask, struct ib_qp_init_attr *);
+int qedr_destroy_qp(struct ib_qp *ibqp);
+
+struct ib_ah *qedr_create_ah(struct ib_pd *ibpd, struct ib_ah_attr *attr);
+int qedr_destroy_ah(struct ib_ah *ibah);
+
+int qedr_dereg_mr(struct ib_mr *);
+struct ib_mr *qedr_get_dma_mr(struct ib_pd *, int acc);
+
+struct ib_mr *qedr_reg_user_mr(struct ib_pd *, u64 start, u64 length,
+                              u64 virt, int acc, struct ib_udata *);
+
+int qedr_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg,
+                  int sg_nents, unsigned int *sg_offset);
+
+struct ib_mr *qedr_alloc_mr(struct ib_pd *pd, enum ib_mr_type mr_type,
+                           u32 max_num_sg);
+int qedr_poll_cq(struct ib_cq *, int num_entries, struct ib_wc *wc);
+int qedr_post_send(struct ib_qp *, struct ib_send_wr *,
+                  struct ib_send_wr **bad_wr);
+int qedr_post_recv(struct ib_qp *, struct ib_recv_wr *,
+                  struct ib_recv_wr **bad_wr);
+int qedr_process_mad(struct ib_device *ibdev, int process_mad_flags,
+                    u8 port_num, const struct ib_wc *in_wc,
+                    const struct ib_grh *in_grh,
+                    const struct ib_mad_hdr *in_mad,
+                    size_t in_mad_size, struct ib_mad_hdr *out_mad,
+                    size_t *out_mad_size, u16 *out_mad_pkey_index);
+
+int qedr_port_immutable(struct ib_device *ibdev, u8 port_num,
+                       struct ib_port_immutable *immutable);
+#endif
index 2d2b94fd3633bff1ddbed16d077ee436bbe0ae58..75f08624ac052abed2347cb462990c0545c8d828 100644 (file)
@@ -67,7 +67,8 @@ static int __qib_get_user_pages(unsigned long start_page, size_t num_pages,
 
        for (got = 0; got < num_pages; got += ret) {
                ret = get_user_pages(start_page + got * PAGE_SIZE,
-                                    num_pages - got, 1, 1,
+                                    num_pages - got,
+                                    FOLL_WRITE | FOLL_FORCE,
                                     p + got, NULL);
                if (ret < 0)
                        goto bail_release;
index a0b6ebee4d8a047e2fdffb8bbd831e5c36107fec..1ccee6ea5bc3092f196689c4481b21d9f27b796c 100644 (file)
@@ -111,6 +111,7 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable,
        int i;
        int flags;
        dma_addr_t pa;
+       unsigned int gup_flags;
 
        if (!can_do_mlock())
                return -EPERM;
@@ -135,6 +136,8 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable,
 
        flags = IOMMU_READ | IOMMU_CACHE;
        flags |= (writable) ? IOMMU_WRITE : 0;
+       gup_flags = FOLL_WRITE;
+       gup_flags |= (writable) ? 0 : FOLL_FORCE;
        cur_base = addr & PAGE_MASK;
        ret = 0;
 
@@ -142,7 +145,7 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable,
                ret = get_user_pages(cur_base,
                                        min_t(unsigned long, npages,
                                        PAGE_SIZE / sizeof(struct page *)),
-                                       1, !writable, page_list, NULL);
+                                       gup_flags, page_list, NULL);
 
                if (ret < 0)
                        goto out;
index 936f07a4e35f41265de96e84c2bab08f13544ad6..6d7de9bfed9a7f2bdf872474f156613740632539 100644 (file)
@@ -103,6 +103,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
                                           6-byte ALPS packet */
 #define ALPS_STICK_BITS                0x100   /* separate stick button bits */
 #define ALPS_BUTTONPAD         0x200   /* device is a clickpad */
+#define ALPS_DUALPOINT_WITH_PRESSURE   0x400   /* device can report trackpoint pressure */
 
 static const struct alps_model_info alps_model_data[] = {
        { { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },      /* Toshiba Salellite Pro M10 */
@@ -1156,15 +1157,28 @@ static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte)
 {
        unsigned char pkt_id = SS4_PACKET_ID_IDLE;
 
-       if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 &&
-           (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && byte[5] == 0x00) {
-               pkt_id = SS4_PACKET_ID_IDLE;
-       } else if (!(byte[3] & 0x10)) {
-               pkt_id = SS4_PACKET_ID_ONE;
-       } else if (!(byte[3] & 0x20)) {
+       switch (byte[3] & 0x30) {
+       case 0x00:
+               if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 &&
+                   (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 &&
+                   byte[5] == 0x00) {
+                       pkt_id = SS4_PACKET_ID_IDLE;
+               } else {
+                       pkt_id = SS4_PACKET_ID_ONE;
+               }
+               break;
+       case 0x10:
+               /* two-finger finger positions */
                pkt_id = SS4_PACKET_ID_TWO;
-       } else {
+               break;
+       case 0x20:
+               /* stick pointer */
+               pkt_id = SS4_PACKET_ID_STICK;
+               break;
+       case 0x30:
+               /* third and fourth finger positions */
                pkt_id = SS4_PACKET_ID_MULTI;
+               break;
        }
 
        return pkt_id;
@@ -1185,7 +1199,13 @@ static int alps_decode_ss4_v2(struct alps_fields *f,
                f->mt[0].x = SS4_1F_X_V2(p);
                f->mt[0].y = SS4_1F_Y_V2(p);
                f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f;
-               f->fingers = 1;
+               /*
+                * When a button is held the device will give us events
+                * with x, y, and pressure of 0. This causes annoying jumps
+                * if a touch is released while the button is held.
+                * Handle this by claiming zero contacts.
+                */
+               f->fingers = f->pressure > 0 ? 1 : 0;
                f->first_mp = 0;
                f->is_mp = 0;
                break;
@@ -1246,16 +1266,40 @@ static int alps_decode_ss4_v2(struct alps_fields *f,
                }
                break;
 
+       case SS4_PACKET_ID_STICK:
+               if (!(priv->flags & ALPS_DUALPOINT)) {
+                       psmouse_warn(psmouse,
+                                    "Rejected trackstick packet from non DualPoint device");
+               } else {
+                       int x = (s8)(((p[0] & 1) << 7) | (p[1] & 0x7f));
+                       int y = (s8)(((p[3] & 1) << 7) | (p[2] & 0x7f));
+                       int pressure = (s8)(p[4] & 0x7f);
+
+                       input_report_rel(priv->dev2, REL_X, x);
+                       input_report_rel(priv->dev2, REL_Y, -y);
+                       input_report_abs(priv->dev2, ABS_PRESSURE, pressure);
+               }
+               break;
+
        case SS4_PACKET_ID_IDLE:
        default:
                memset(f, 0, sizeof(struct alps_fields));
                break;
        }
 
-       f->left = !!(SS4_BTN_V2(p) & 0x01);
-       if (!(priv->flags & ALPS_BUTTONPAD)) {
-               f->right = !!(SS4_BTN_V2(p) & 0x02);
-               f->middle = !!(SS4_BTN_V2(p) & 0x04);
+       /* handle buttons */
+       if (pkt_id == SS4_PACKET_ID_STICK) {
+               f->ts_left = !!(SS4_BTN_V2(p) & 0x01);
+               if (!(priv->flags & ALPS_BUTTONPAD)) {
+                       f->ts_right = !!(SS4_BTN_V2(p) & 0x02);
+                       f->ts_middle = !!(SS4_BTN_V2(p) & 0x04);
+               }
+       } else {
+               f->left = !!(SS4_BTN_V2(p) & 0x01);
+               if (!(priv->flags & ALPS_BUTTONPAD)) {
+                       f->right = !!(SS4_BTN_V2(p) & 0x02);
+                       f->middle = !!(SS4_BTN_V2(p) & 0x04);
+               }
        }
 
        return 0;
@@ -1266,6 +1310,7 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse)
        struct alps_data *priv = psmouse->private;
        unsigned char *packet = psmouse->packet;
        struct input_dev *dev = psmouse->dev;
+       struct input_dev *dev2 = priv->dev2;
        struct alps_fields *f = &priv->f;
 
        memset(f, 0, sizeof(struct alps_fields));
@@ -1311,6 +1356,13 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse)
 
        input_report_abs(dev, ABS_PRESSURE, f->pressure);
        input_sync(dev);
+
+       if (priv->flags & ALPS_DUALPOINT) {
+               input_report_key(dev2, BTN_LEFT, f->ts_left);
+               input_report_key(dev2, BTN_RIGHT, f->ts_right);
+               input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
+               input_sync(dev2);
+       }
 }
 
 static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse)
@@ -2695,6 +2747,10 @@ static int alps_set_protocol(struct psmouse *psmouse,
                if (alps_set_defaults_ss4_v2(psmouse, priv))
                        return -EIO;
 
+               if (priv->fw_ver[1] == 0x1)
+                       priv->flags |= ALPS_DUALPOINT |
+                                       ALPS_DUALPOINT_WITH_PRESSURE;
+
                break;
        }
 
@@ -2767,6 +2823,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
                } else if (e7[0] == 0x73 && e7[1] == 0x03 &&
                           e7[2] == 0x14 && ec[1] == 0x02) {
                        protocol = &alps_v8_protocol_data;
+               } else if (e7[0] == 0x73 && e7[1] == 0x03 &&
+                          e7[2] == 0x28 && ec[1] == 0x01) {
+                       protocol = &alps_v8_protocol_data;
                } else {
                        psmouse_dbg(psmouse,
                                    "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
@@ -2949,6 +3008,10 @@ int alps_init(struct psmouse *psmouse)
 
                input_set_capability(dev2, EV_REL, REL_X);
                input_set_capability(dev2, EV_REL, REL_Y);
+               if (priv->flags & ALPS_DUALPOINT_WITH_PRESSURE) {
+                       input_set_capability(dev2, EV_ABS, ABS_PRESSURE);
+                       input_set_abs_params(dev2, ABS_PRESSURE, 0, 127, 0, 0);
+               }
                input_set_capability(dev2, EV_KEY, BTN_LEFT);
                input_set_capability(dev2, EV_KEY, BTN_RIGHT);
                input_set_capability(dev2, EV_KEY, BTN_MIDDLE);
index d37f814dc4478186eb3db8694d84bb2b0e7a9175..b9417e2d7ad3a7ba01aff4d7c5504a619090b0b5 100644 (file)
  *  or there's button activities.
  * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad
  * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad
+ * SS4_PACKET_ID_STICK: A stick pointer packet
 */
 enum SS4_PACKET_ID {
        SS4_PACKET_ID_IDLE = 0,
        SS4_PACKET_ID_ONE,
        SS4_PACKET_ID_TWO,
        SS4_PACKET_ID_MULTI,
+       SS4_PACKET_ID_STICK,
 };
 
 #define SS4_COUNT_PER_ELECTRODE                256
index 08e252a424802df0885711509e3e26528ec7b7ba..db7d1d666ac1092e23806793e3c3b4b8e292a766 100644 (file)
@@ -1134,7 +1134,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
  * System76 Pangolin       0x250f01        ?               2 hw buttons
  * (*) + 3 trackpoint buttons
  * (**) + 0 trackpoint buttons
- * Note: Lenovo L430 and Lenovo L430 have the same fw_version/caps
+ * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps
  */
 static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
 {
@@ -1159,6 +1159,13 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
                },
        },
+       {
+               /* Fujitsu H760 also has a middle button */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
+               },
+       },
 #endif
        { }
 };
@@ -1503,10 +1510,10 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
                },
        },
        {
-               /* Fujitsu LIFEBOOK E554  does not work with crc_enabled == 0 */
+               /* Fujitsu H760 does not work with crc_enabled == 0 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
                },
        },
        {
@@ -1516,6 +1523,20 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E544"),
                },
        },
+       {
+               /* Fujitsu LIFEBOOK E554  does not work with crc_enabled == 0 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
+               },
+       },
+       {
+               /* Fujitsu LIFEBOOK E556 does not work with crc_enabled == 0 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E556"),
+               },
+       },
        {
                /* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */
                .matches = {
index 6f2e0e4f0296999885a40e382bc319ba15fc0253..1ebc2c1debae31e5d1479466085a9d7fbcfc586f 100644 (file)
@@ -221,6 +221,21 @@ static const struct of_device_id rmi_i2c_of_match[] = {
 MODULE_DEVICE_TABLE(of, rmi_i2c_of_match);
 #endif
 
+static void rmi_i2c_regulator_bulk_disable(void *data)
+{
+       struct rmi_i2c_xport *rmi_i2c = data;
+
+       regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
+                              rmi_i2c->supplies);
+}
+
+static void rmi_i2c_unregister_transport(void *data)
+{
+       struct rmi_i2c_xport *rmi_i2c = data;
+
+       rmi_unregister_transport_device(&rmi_i2c->xport);
+}
+
 static int rmi_i2c_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
@@ -264,6 +279,12 @@ static int rmi_i2c_probe(struct i2c_client *client,
        if (retval < 0)
                return retval;
 
+       retval = devm_add_action_or_reset(&client->dev,
+                                         rmi_i2c_regulator_bulk_disable,
+                                         rmi_i2c);
+       if (retval)
+               return retval;
+
        of_property_read_u32(client->dev.of_node, "syna,startup-delay-ms",
                             &rmi_i2c->startup_delay);
 
@@ -294,6 +315,11 @@ static int rmi_i2c_probe(struct i2c_client *client,
                        client->addr);
                return retval;
        }
+       retval = devm_add_action_or_reset(&client->dev,
+                                         rmi_i2c_unregister_transport,
+                                         rmi_i2c);
+       if (retval)
+               return retval;
 
        retval = rmi_i2c_init_irq(client);
        if (retval < 0)
@@ -304,17 +330,6 @@ static int rmi_i2c_probe(struct i2c_client *client,
        return 0;
 }
 
-static int rmi_i2c_remove(struct i2c_client *client)
-{
-       struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
-
-       rmi_unregister_transport_device(&rmi_i2c->xport);
-       regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
-                              rmi_i2c->supplies);
-
-       return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int rmi_i2c_suspend(struct device *dev)
 {
@@ -431,7 +446,6 @@ static struct i2c_driver rmi_i2c_driver = {
        },
        .id_table       = rmi_id,
        .probe          = rmi_i2c_probe,
-       .remove         = rmi_i2c_remove,
 };
 
 module_i2c_driver(rmi_i2c_driver);
index 55bd1b34970c90a62005264e257ed774280cfd22..4ebef607e2141ad574fe161366308c08e101dc68 100644 (file)
@@ -396,6 +396,13 @@ static inline int rmi_spi_of_probe(struct spi_device *spi,
 }
 #endif
 
+static void rmi_spi_unregister_transport(void *data)
+{
+       struct rmi_spi_xport *rmi_spi = data;
+
+       rmi_unregister_transport_device(&rmi_spi->xport);
+}
+
 static int rmi_spi_probe(struct spi_device *spi)
 {
        struct rmi_spi_xport *rmi_spi;
@@ -464,6 +471,11 @@ static int rmi_spi_probe(struct spi_device *spi)
                dev_err(&spi->dev, "failed to register transport.\n");
                return retval;
        }
+       retval = devm_add_action_or_reset(&spi->dev,
+                                         rmi_spi_unregister_transport,
+                                         rmi_spi);
+       if (retval)
+               return retval;
 
        retval = rmi_spi_init_irq(spi);
        if (retval < 0)
@@ -473,15 +485,6 @@ static int rmi_spi_probe(struct spi_device *spi)
        return 0;
 }
 
-static int rmi_spi_remove(struct spi_device *spi)
-{
-       struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi);
-
-       rmi_unregister_transport_device(&rmi_spi->xport);
-
-       return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int rmi_spi_suspend(struct device *dev)
 {
@@ -577,7 +580,6 @@ static struct spi_driver rmi_spi_driver = {
        },
        .id_table       = rmi_id,
        .probe          = rmi_spi_probe,
-       .remove         = rmi_spi_remove,
 };
 
 module_spi_driver(rmi_spi_driver);
index a5eed2ade53de3a4e1d06776288ec17baba52530..34da81c006b6d80d3df98feffc7651d8eaa3f2c8 100644 (file)
@@ -81,7 +81,7 @@ static inline int i8042_platform_init(void)
                return -EBUSY;
 #endif
 
-       i8042_reset = 1;
+       i8042_reset = I8042_RESET_ALWAYS;
        return 0;
 }
 
index ee1ad27d6ed06ef70370f3096352b37acb1c1368..08a1c10a1448d12db092a1c3f7525e4f5f1b76a2 100644 (file)
@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void)
                return -EBUSY;
 #endif
 
-       i8042_reset = 1;
+       i8042_reset = I8042_RESET_ALWAYS;
 
        return 0;
 }
index f708c75d16f1d919f4e054c94437fb6e4e587866..1aabea43329edf56f2067c73eafca4d140d3ac4b 100644 (file)
@@ -44,7 +44,7 @@ static inline void i8042_write_command(int val)
 
 static inline int i8042_platform_init(void)
 {
-       i8042_reset = 1;
+       i8042_reset = I8042_RESET_ALWAYS;
        return 0;
 }
 
index afcd1c1a05b272b1d615b481f2de9c0a812a2889..6231d63860ee324031d36e6e1c42dd57407dc27f 100644 (file)
@@ -130,7 +130,7 @@ static int __init i8042_platform_init(void)
                }
        }
 
-       i8042_reset = 1;
+       i8042_reset = I8042_RESET_ALWAYS;
 
        return 0;
 }
index 73f5cc124a3606a5cb73406e207d8a27fd4c339e..455747552f858a8819ec18d03317980352e514ea 100644 (file)
@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void)
        if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042"))
                return -EBUSY;
 
-       i8042_reset = 1;
+       i8042_reset = I8042_RESET_ALWAYS;
        return 0;
 }
 
index 68f5f4a0f1e72f10b35e45243218e6cfbf3d586e..f4bfb4b2d50a356336fdab08124f52085a82f24f 100644 (file)
@@ -510,6 +510,90 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
        { }
 };
 
+/*
+ * On some Asus laptops, just running self tests cause problems.
+ */
+static const struct dmi_system_id i8042_dmi_noselftest_table[] = {
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "R409L"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"),
+               },
+       },
+       { }
+};
 static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
        {
                /* MSI Wind U-100 */
@@ -1072,12 +1156,18 @@ static int __init i8042_platform_init(void)
                return retval;
 
 #if defined(__ia64__)
-        i8042_reset = true;
+        i8042_reset = I8042_RESET_ALWAYS;
 #endif
 
 #ifdef CONFIG_X86
-       if (dmi_check_system(i8042_dmi_reset_table))
-               i8042_reset = true;
+       /* Honor module parameter when value is not default */
+       if (i8042_reset == I8042_RESET_DEFAULT) {
+               if (dmi_check_system(i8042_dmi_reset_table))
+                       i8042_reset = I8042_RESET_ALWAYS;
+
+               if (dmi_check_system(i8042_dmi_noselftest_table))
+                       i8042_reset = I8042_RESET_NEVER;
+       }
 
        if (dmi_check_system(i8042_dmi_noloop_table))
                i8042_noloop = true;
index 405252a884dd41e233da4399ab109f213becb85a..89abfdb539ac750ff50eca67f77b2fe6898ff6f0 100644 (file)
@@ -48,9 +48,39 @@ static bool i8042_unlock;
 module_param_named(unlock, i8042_unlock, bool, 0);
 MODULE_PARM_DESC(unlock, "Ignore keyboard lock.");
 
-static bool i8042_reset;
-module_param_named(reset, i8042_reset, bool, 0);
-MODULE_PARM_DESC(reset, "Reset controller during init and cleanup.");
+enum i8042_controller_reset_mode {
+       I8042_RESET_NEVER,
+       I8042_RESET_ALWAYS,
+       I8042_RESET_ON_S2RAM,
+#define I8042_RESET_DEFAULT    I8042_RESET_ON_S2RAM
+};
+static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT;
+static int i8042_set_reset(const char *val, const struct kernel_param *kp)
+{
+       enum i8042_controller_reset_mode *arg = kp->arg;
+       int error;
+       bool reset;
+
+       if (val) {
+               error = kstrtobool(val, &reset);
+               if (error)
+                       return error;
+       } else {
+               reset = true;
+       }
+
+       *arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER;
+       return 0;
+}
+
+static const struct kernel_param_ops param_ops_reset_param = {
+       .flags = KERNEL_PARAM_OPS_FL_NOARG,
+       .set = i8042_set_reset,
+};
+#define param_check_reset_param(name, p)       \
+       __param_check(name, p, enum i8042_controller_reset_mode)
+module_param_named(reset, i8042_reset, reset_param, 0);
+MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both");
 
 static bool i8042_direct;
 module_param_named(direct, i8042_direct, bool, 0);
@@ -1019,7 +1049,7 @@ static int i8042_controller_init(void)
  * Reset the controller and reset CRT to the original value set by BIOS.
  */
 
-static void i8042_controller_reset(bool force_reset)
+static void i8042_controller_reset(bool s2r_wants_reset)
 {
        i8042_flush();
 
@@ -1044,8 +1074,10 @@ static void i8042_controller_reset(bool force_reset)
  * Reset the controller if requested.
  */
 
-       if (i8042_reset || force_reset)
+       if (i8042_reset == I8042_RESET_ALWAYS ||
+           (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
                i8042_controller_selftest();
+       }
 
 /*
  * Restore the original control register setting.
@@ -1110,7 +1142,7 @@ static void i8042_dritek_enable(void)
  * before suspending.
  */
 
-static int i8042_controller_resume(bool force_reset)
+static int i8042_controller_resume(bool s2r_wants_reset)
 {
        int error;
 
@@ -1118,7 +1150,8 @@ static int i8042_controller_resume(bool force_reset)
        if (error)
                return error;
 
-       if (i8042_reset || force_reset) {
+       if (i8042_reset == I8042_RESET_ALWAYS ||
+           (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
                error = i8042_controller_selftest();
                if (error)
                        return error;
@@ -1195,7 +1228,7 @@ static int i8042_pm_resume_noirq(struct device *dev)
 
 static int i8042_pm_resume(struct device *dev)
 {
-       bool force_reset;
+       bool want_reset;
        int i;
 
        for (i = 0; i < I8042_NUM_PORTS; i++) {
@@ -1218,9 +1251,9 @@ static int i8042_pm_resume(struct device *dev)
         * off control to the platform firmware, otherwise we can simply restore
         * the mode.
         */
-       force_reset = pm_resume_via_firmware();
+       want_reset = pm_resume_via_firmware();
 
-       return i8042_controller_resume(force_reset);
+       return i8042_controller_resume(want_reset);
 }
 
 static int i8042_pm_thaw(struct device *dev)
@@ -1482,7 +1515,7 @@ static int __init i8042_probe(struct platform_device *dev)
 
        i8042_platform_device = dev;
 
-       if (i8042_reset) {
+       if (i8042_reset == I8042_RESET_ALWAYS) {
                error = i8042_controller_selftest();
                if (error)
                        return error;
index fb5fb9140ca9536c59f0163ccd4cb5e8b8f4d9ac..552a3773f79d0b7815d6d180f7aabc9083ff6c92 100644 (file)
@@ -157,6 +157,7 @@ struct mip4_ts {
 
        char phys[32];
        char product_name[16];
+       char ic_name[4];
 
        unsigned int max_x;
        unsigned int max_y;
@@ -263,6 +264,18 @@ static int mip4_query_device(struct mip4_ts *ts)
                dev_dbg(&ts->client->dev, "product name: %.*s\n",
                        (int)sizeof(ts->product_name), ts->product_name);
 
+       /* IC name */
+       cmd[0] = MIP4_R0_INFO;
+       cmd[1] = MIP4_R1_INFO_IC_NAME;
+       error = mip4_i2c_xfer(ts, cmd, sizeof(cmd),
+                             ts->ic_name, sizeof(ts->ic_name));
+       if (error)
+               dev_warn(&ts->client->dev,
+                        "Failed to retrieve IC name: %d\n", error);
+       else
+               dev_dbg(&ts->client->dev, "IC name: %.*s\n",
+                       (int)sizeof(ts->ic_name), ts->ic_name);
+
        /* Firmware version */
        error = mip4_get_fw_version(ts);
        if (error)
@@ -1326,7 +1339,7 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev,
         * paired with current firmware in the chip.
         */
        count = snprintf(buf, PAGE_SIZE, "%.*s\n",
-               (int)sizeof(ts->product_name), ts->product_name);
+                        (int)sizeof(ts->product_name), ts->product_name);
 
        mutex_unlock(&ts->input->mutex);
 
@@ -1335,9 +1348,30 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev,
 
 static DEVICE_ATTR(hw_version, S_IRUGO, mip4_sysfs_read_hw_version, NULL);
 
+static ssize_t mip4_sysfs_read_ic_name(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct mip4_ts *ts = i2c_get_clientdata(client);
+       size_t count;
+
+       mutex_lock(&ts->input->mutex);
+
+       count = snprintf(buf, PAGE_SIZE, "%.*s\n",
+                        (int)sizeof(ts->ic_name), ts->ic_name);
+
+       mutex_unlock(&ts->input->mutex);
+
+       return count;
+}
+
+static DEVICE_ATTR(ic_name, S_IRUGO, mip4_sysfs_read_ic_name, NULL);
+
 static struct attribute *mip4_attrs[] = {
        &dev_attr_fw_version.attr,
        &dev_attr_hw_version.attr,
+       &dev_attr_ic_name.attr,
        &dev_attr_update_fw.attr,
        NULL,
 };
@@ -1538,6 +1572,6 @@ static struct i2c_driver mip4_driver = {
 module_i2c_driver(mip4_driver);
 
 MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen");
-MODULE_VERSION("2016.03.12");
+MODULE_VERSION("2016.09.28");
 MODULE_AUTHOR("Sangwon Jee <jeesw@melfas.com>");
 MODULE_LICENSE("GPL");
index efbf0e4304b7073d5aa12bfa97a1d727aace81b7..ebc2b0b15f677e4bac6afef3319a4d9467cc5bd6 100644 (file)
@@ -85,7 +85,7 @@ static void nps400_irq_eoi_global(struct irq_data *irqd)
        nps_ack_gic();
 }
 
-static void nps400_irq_eoi(struct irq_data *irqd)
+static void nps400_irq_ack(struct irq_data *irqd)
 {
        unsigned int __maybe_unused irq = irqd_to_hwirq(irqd);
 
@@ -103,7 +103,7 @@ static struct irq_chip nps400_irq_chip_percpu = {
        .name           = "NPS400 IC",
        .irq_mask       = nps400_irq_mask,
        .irq_unmask     = nps400_irq_unmask,
-       .irq_eoi        = nps400_irq_eoi,
+       .irq_ack        = nps400_irq_ack,
 };
 
 static int nps400_irq_map(struct irq_domain *d, unsigned int virq,
index 9b81bd8b929c0bf925d6e3fe578d03b0c2af7afd..19d642eae096b2495f5707470ea5f857d2f9b876 100644 (file)
@@ -153,7 +153,7 @@ static void gic_enable_redist(bool enable)
                        return; /* No PM support in this redistributor */
        }
 
-       while (count--) {
+       while (--count) {
                val = readl_relaxed(rbase + GICR_WAKER);
                if (enable ^ (bool)(val & GICR_WAKER_ChildrenAsleep))
                        break;
index 6b304eb39bd22c96b64c6a99629ef4195db721b5..1aec12c6d9ac83cb4338d4c0c44a78b83c1e01d8 100644 (file)
@@ -38,6 +38,7 @@ static void disable_8259A_irq(struct irq_data *d);
 static void enable_8259A_irq(struct irq_data *d);
 static void mask_and_ack_8259A(struct irq_data *d);
 static void init_8259A(int auto_eoi);
+static int (*i8259_poll)(void) = i8259_irq;
 
 static struct irq_chip i8259A_chip = {
        .name                   = "XT-PIC",
@@ -51,6 +52,11 @@ static struct irq_chip i8259A_chip = {
  * 8259A PIC functions to handle ISA devices:
  */
 
+void i8259_set_poll(int (*poll)(void))
+{
+       i8259_poll = poll;
+}
+
 /*
  * This contains the irq mask for both 8259A irq controllers,
  */
@@ -89,24 +95,6 @@ static void enable_8259A_irq(struct irq_data *d)
        raw_spin_unlock_irqrestore(&i8259A_lock, flags);
 }
 
-int i8259A_irq_pending(unsigned int irq)
-{
-       unsigned int mask;
-       unsigned long flags;
-       int ret;
-
-       irq -= I8259A_IRQ_BASE;
-       mask = 1 << irq;
-       raw_spin_lock_irqsave(&i8259A_lock, flags);
-       if (irq < 8)
-               ret = inb(PIC_MASTER_CMD) & mask;
-       else
-               ret = inb(PIC_SLAVE_CMD) & (mask >> 8);
-       raw_spin_unlock_irqrestore(&i8259A_lock, flags);
-
-       return ret;
-}
-
 void make_8259A_irq(unsigned int irq)
 {
        disable_irq_nosync(irq);
@@ -355,7 +343,7 @@ void __init init_i8259_irqs(void)
 static void i8259_irq_dispatch(struct irq_desc *desc)
 {
        struct irq_domain *domain = irq_desc_get_handler_data(desc);
-       int hwirq = i8259_irq();
+       int hwirq = i8259_poll();
        unsigned int irq;
 
        if (hwirq < 0)
@@ -370,13 +358,15 @@ int __init i8259_of_init(struct device_node *node, struct device_node *parent)
        struct irq_domain *domain;
        unsigned int parent_irq;
 
+       domain = __init_i8259_irqs(node);
+
        parent_irq = irq_of_parse_and_map(node, 0);
        if (!parent_irq) {
                pr_err("Failed to map i8259 parent IRQ\n");
+               irq_domain_remove(domain);
                return -ENODEV;
        }
 
-       domain = __init_i8259_irqs(node);
        irq_set_chained_handler_and_data(parent_irq, i8259_irq_dispatch,
                                         domain);
        return 0;
index 84b01dec277dfee6f35c286c43448ed258a48199..033bccb41455c46c32d5473fa46940c2770973e9 100644 (file)
 
 static struct irq_chip jcore_aic;
 
+/*
+ * The J-Core AIC1 and AIC2 are cpu-local interrupt controllers and do
+ * not distinguish or use distinct irq number ranges for per-cpu event
+ * interrupts (timer, IPI). Since information to determine whether a
+ * particular irq number should be treated as per-cpu is not available
+ * at mapping time, we use a wrapper handler function which chooses
+ * the right handler at runtime based on whether IRQF_PERCPU was used
+ * when requesting the irq.
+ */
+
+static void handle_jcore_irq(struct irq_desc *desc)
+{
+       if (irqd_is_per_cpu(irq_desc_get_irq_data(desc)))
+               handle_percpu_irq(desc);
+       else
+               handle_simple_irq(desc);
+}
+
 static int jcore_aic_irqdomain_map(struct irq_domain *d, unsigned int irq,
                                   irq_hw_number_t hwirq)
 {
        struct irq_chip *aic = d->host_data;
 
-       irq_set_chip_and_handler(irq, aic, handle_simple_irq);
+       irq_set_chip_and_handler(irq, aic, handle_jcore_irq);
 
        return 0;
 }
index 4769469fe842964574eae3a72b812f579ab5f63f..2c9232ef7baa4b57efd020547d0b2af3f42199d4 100644 (file)
@@ -124,8 +124,8 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
        }
 
        /* Get user pages for DMA Xfer */
-       err = get_user_pages_unlocked(user_dma.uaddr, user_dma.page_count, 0,
-                       1, dma->map);
+       err = get_user_pages_unlocked(user_dma.uaddr, user_dma.page_count,
+                       dma->map, FOLL_FORCE);
 
        if (user_dma.page_count != err) {
                IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
index b094054cda6e55b64852598d6d22dd6d57e6cdaa..f7299d3d82449ddaefc1ee76fd3f8cb33650e5b4 100644 (file)
@@ -76,11 +76,12 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
 
        /* Get user pages for DMA Xfer */
        y_pages = get_user_pages_unlocked(y_dma.uaddr,
-                       y_dma.page_count, 0, 1, &dma->map[0]);
+                       y_dma.page_count, &dma->map[0], FOLL_FORCE);
        uv_pages = 0; /* silence gcc. value is set and consumed only if: */
        if (y_pages == y_dma.page_count) {
                uv_pages = get_user_pages_unlocked(uv_dma.uaddr,
-                               uv_dma.page_count, 0, 1, &dma->map[y_pages]);
+                               uv_dma.page_count, &dma->map[y_pages],
+                               FOLL_FORCE);
        }
 
        if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) {
index e668dde6d85722d2b68d7f399452d22bbf6d8342..a31b95cb3b09c0623be42762cea8d4dc08c99170 100644 (file)
@@ -214,7 +214,7 @@ static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp,
        if (!vec)
                return -ENOMEM;
 
-       ret = get_vaddr_frames(virtp, 1, true, false, vec);
+       ret = get_vaddr_frames(virtp, 1, FOLL_WRITE, vec);
        if (ret != 1) {
                frame_vector_destroy(vec);
                return -EINVAL;
index 29b3436d0910fbd0c1a591b0aea408dc34aca74a..367523a3c774c29b4e015419d06a9c4c8275eeaa 100644 (file)
@@ -27,7 +27,7 @@ config VIDEO_FIXED_MINOR_RANGES
 
 config VIDEO_PCI_SKELETON
        tristate "Skeleton PCI V4L2 driver"
-       depends on PCI && BUILD_DOCSRC
+       depends on PCI
        depends on VIDEO_V4L2 && VIDEOBUF2_CORE
        depends on VIDEOBUF2_MEMOPS && VIDEOBUF2_DMA_CONTIG
        ---help---
index f300f060b3f34cdfeb8b1e12b90d9ab345c6b629..1db0af6c7f94810bf9270b0ebb5961de6b1802a5 100644 (file)
@@ -156,6 +156,7 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
 {
        unsigned long first, last;
        int err, rw = 0;
+       unsigned int flags = FOLL_FORCE;
 
        dma->direction = direction;
        switch (dma->direction) {
@@ -178,12 +179,14 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
        if (NULL == dma->pages)
                return -ENOMEM;
 
+       if (rw == READ)
+               flags |= FOLL_WRITE;
+
        dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n",
                data, size, dma->nr_pages);
 
        err = get_user_pages(data & PAGE_MASK, dma->nr_pages,
-                            rw == READ, 1, /* force */
-                            dma->pages, NULL);
+                            flags, dma->pages, NULL);
 
        if (err != dma->nr_pages) {
                dma->nr_pages = (err >= 0) ? err : 0;
index 3c3b517f1d1cacb5a7131263cd80c112bd22d619..1cd322e939c70520e9e915575590bf516c0e46ee 100644 (file)
@@ -42,6 +42,10 @@ struct frame_vector *vb2_create_framevec(unsigned long start,
        unsigned long first, last;
        unsigned long nr;
        struct frame_vector *vec;
+       unsigned int flags = FOLL_FORCE;
+
+       if (write)
+               flags |= FOLL_WRITE;
 
        first = start >> PAGE_SHIFT;
        last = (start + length - 1) >> PAGE_SHIFT;
@@ -49,7 +53,7 @@ struct frame_vector *vb2_create_framevec(unsigned long start,
        vec = frame_vector_create(nr);
        if (!vec)
                return ERR_PTR(-ENOMEM);
-       ret = get_vaddr_frames(start & PAGE_MASK, nr, write, true, vec);
+       ret = get_vaddr_frames(start & PAGE_MASK, nr, flags, vec);
        if (ret < 0)
                goto out_destroy;
        /* We accept only complete set of PFNs */
index e0203b1a20fd17fe88d9db3dc882d4d64eed2cc5..f806a4471eb913f388042886de29ec8de996cbf5 100644 (file)
@@ -1396,8 +1396,7 @@ retry:
                pinned_pages->nr_pages = get_user_pages(
                                (u64)addr,
                                nr_pages,
-                               !!(prot & SCIF_PROT_WRITE),
-                               0,
+                               (prot & SCIF_PROT_WRITE) ? FOLL_WRITE : 0,
                                pinned_pages->pages,
                                NULL);
                up_write(&mm->mmap_sem);
index a2d97b9b17e3463cebea03c70ec062cafcdac08c..6fb773dbcd0c3233d62136dcf673afb7b80efcea 100644 (file)
@@ -198,7 +198,7 @@ static int non_atomic_pte_lookup(struct vm_area_struct *vma,
 #else
        *pageshift = PAGE_SHIFT;
 #endif
-       if (get_user_pages(vaddr, 1, write, 0, &page, NULL) <= 0)
+       if (get_user_pages(vaddr, 1, write ? FOLL_WRITE : 0, &page, NULL) <= 0)
                return -EFAULT;
        *paddr = page_to_phys(page);
        put_page(page);
index bddb198c0b74c15bdcdaed635270f9f3c3ab00f3..380a64115a982bb15d1f6bbe5c4122711aa79c1a 100644 (file)
@@ -693,7 +693,7 @@ static int cn23xx_enable_io_queues(struct octeon_device *oct)
                                while ((reg_val & CN23XX_PKT_INPUT_CTL_RST) &&
                                       !(reg_val &
                                         CN23XX_PKT_INPUT_CTL_QUIET) &&
-                                      loop--) {
+                                      --loop) {
                                        reg_val = octeon_read_csr64(
                                            oct,
                                            CN23XX_SLI_IQ_PKT_CONTROL64(q_no));
index e28d960997af3189885161bd40c355b28aee84af..2d0cb609adc33415ab0c724ff266e1f4a145d694 100644 (file)
@@ -207,6 +207,7 @@ 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);
+       u8 port_num;
 
        assert(mac_cb);
 
@@ -221,8 +222,11 @@ static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr)
                return ret;
        }
 
-       ret = hns_mac_set_multi(mac_cb, DSAF_BASE_INNER_PORT_NUM,
-                               mac_addr, true);
+       ret = hns_mac_get_inner_port_num(mac_cb, handle->vf_id, &port_num);
+       if (ret)
+               return ret;
+
+       ret = hns_mac_set_multi(mac_cb, port_num, mac_addr, true);
        if (ret)
                dev_err(handle->owner_dev,
                        "mac add mul_mac:%pM port%d  fail, ret = %#x!\n",
@@ -678,9 +682,6 @@ static int hns_ae_config_loopback(struct hnae_handle *handle,
                ret = -EINVAL;
        }
 
-       if (!ret)
-               hns_dsaf_set_inner_lb(mac_cb->dsaf_dev, mac_cb->mac_id, en);
-
        return ret;
 }
 
index 22e141005cd92df0ef615ba30ffbcc4a020150f7..ec8c738af726323e92c2416a639806462f0bfe4b 100644 (file)
@@ -141,9 +141,10 @@ void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex)
  *@port_num:port number
  *
  */
-static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb,
-                                     u8 vmid, u8 *port_num)
+int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb, u8 vmid, u8 *port_num)
 {
+       int q_num_per_vf, vf_num_per_port;
+       int vm_queue_id;
        u8 tmp_port;
 
        if (mac_cb->dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE) {
@@ -174,6 +175,12 @@ static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb,
                return -EINVAL;
        }
 
+       q_num_per_vf = mac_cb->dsaf_dev->rcb_common[0]->max_q_per_vf;
+       vf_num_per_port = mac_cb->dsaf_dev->rcb_common[0]->max_vfn;
+
+       vm_queue_id = vmid * q_num_per_vf +
+                       vf_num_per_port * q_num_per_vf * mac_cb->mac_id;
+
        switch (mac_cb->dsaf_dev->dsaf_mode) {
        case DSAF_MODE_ENABLE_FIX:
                tmp_port = 0;
@@ -193,7 +200,7 @@ static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb,
        case DSAF_MODE_DISABLE_6PORT_2VM:
        case DSAF_MODE_DISABLE_6PORT_4VM:
        case DSAF_MODE_DISABLE_6PORT_16VM:
-               tmp_port = vmid;
+               tmp_port = vm_queue_id;
                break;
        default:
                dev_err(mac_cb->dev, "dsaf mode invalid, %s mac%d!\n",
index 4cbdf14f5c163e7405dd323327e8d3dc015a6ae5..d3a1f72ece0e518107de05437fdc4e2a7929ab39 100644 (file)
@@ -461,5 +461,7 @@ 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);
 void hns_mac_set_promisc(struct hns_mac_cb *mac_cb, u8 en);
+int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb,
+                              u8 vmid, u8 *port_num);
 
 #endif /* _HNS_DSAF_MAC_H */
index 8e5b3f51b47b80201d7c447a9f061116b69e141c..8d70377f6624cb5d1cd55ae9916af6f75ee934b4 100644 (file)
@@ -760,16 +760,6 @@ void hns_dsaf_set_promisc_mode(struct dsaf_device *dsaf_dev, u32 en)
                                 DSAF_CFG_MIX_MODE_S, !!en);
 }
 
-void hns_dsaf_set_inner_lb(struct dsaf_device *dsaf_dev, u32 mac_id, u32 en)
-{
-       if (AE_IS_VER1(dsaf_dev->dsaf_ver) ||
-           dsaf_dev->mac_cb[mac_id]->mac_type == HNAE_PORT_DEBUG)
-               return;
-
-       dsaf_set_dev_bit(dsaf_dev, DSAFV2_SERDES_LBK_0_REG + 4 * mac_id,
-                        DSAFV2_SERDES_LBK_EN_B, !!en);
-}
-
 /**
  * hns_dsaf_tbl_stat_en - tbl
  * @dsaf_id: dsa fabric id
index 35df187e66f1909185b010289f4b6009b54c2a10..c494fc52be7496382b092d77ea29f26992c1c6bd 100644 (file)
@@ -466,6 +466,5 @@ void hns_dsaf_get_rx_mac_pause_en(struct dsaf_device *dsaf_dev, int mac_id,
                                  u32 *en);
 int hns_dsaf_set_rx_mac_pause_en(struct dsaf_device *dsaf_dev, int mac_id,
                                 u32 en);
-void hns_dsaf_set_inner_lb(struct dsaf_device *dsaf_dev, u32 mac_id, u32 en);
 
 #endif /* __HNS_DSAF_MAIN_H__ */
index ef1107777c08d58117110cf2c5da08c2dcec3e23..f0ed80d6ef9cd45a8408c987ab4315646098f438 100644 (file)
@@ -543,6 +543,22 @@ int hns_rcb_set_coalesce_usecs(
                        "error: coalesce_usecs setting supports 0~1023us\n");
                return -EINVAL;
        }
+
+       if (!AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver)) {
+               if (timeout == 0)
+                       /* set timeout to 0, Disable gap time */
+                       dsaf_set_reg_field(rcb_common->io_base,
+                                          RCB_INT_GAP_TIME_REG + port_idx * 4,
+                                          PPE_INT_GAPTIME_M, PPE_INT_GAPTIME_B,
+                                          0);
+               else
+                       /* set timeout non 0, restore gap time to 1 */
+                       dsaf_set_reg_field(rcb_common->io_base,
+                                          RCB_INT_GAP_TIME_REG + port_idx * 4,
+                                          PPE_INT_GAPTIME_M, PPE_INT_GAPTIME_B,
+                                          1);
+       }
+
        hns_rcb_set_port_timeout(rcb_common, port_idx, timeout);
        return 0;
 }
index 4b8b803822d1b2ae7d15c599aa7778a728cc3883..878950a42e6c2399a047bf91d3f358aa653d9127 100644 (file)
 #define RCB_CFG_OVERTIME_REG                   0x9300
 #define RCB_CFG_PKTLINE_INT_NUM_REG            0x9304
 #define RCB_CFG_OVERTIME_INT_NUM_REG           0x9308
+#define RCB_INT_GAP_TIME_REG                   0x9400
 #define RCB_PORT_CFG_OVERTIME_REG              0x9430
 
 #define RCB_RING_RX_RING_BASEADDR_L_REG                0x00000
 #define PPE_CNT_CLR_CE_B       0
 #define PPE_CNT_CLR_SNAP_EN_B  1
 
+#define PPE_INT_GAPTIME_B      0
+#define PPE_INT_GAPTIME_M      0x3ff
+
 #define PPE_COMMON_CNT_CLR_CE_B        0
 #define PPE_COMMON_CNT_CLR_SNAP_EN_B   1
 #define RCB_COM_TSO_MODE_B     0
index 059aaeda46b139492b00d645c73e51e4279fb6c6..dff7b60345d8e94b90a53f16d2c9caadde1b449a 100644 (file)
@@ -574,7 +574,6 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
        struct sk_buff *skb;
        struct hnae_desc *desc;
        struct hnae_desc_cb *desc_cb;
-       struct ethhdr *eh;
        unsigned char *va;
        int bnum, length, i;
        int pull_len;
@@ -600,7 +599,6 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
                ring->stats.sw_err_cnt++;
                return -ENOMEM;
        }
-       skb_reset_mac_header(skb);
 
        prefetchw(skb->data);
        length = le16_to_cpu(desc->rx.pkt_len);
@@ -682,14 +680,6 @@ out_bnum_err:
                return -EFAULT;
        }
 
-       /* filter out multicast pkt with the same src mac as this port */
-       eh = eth_hdr(skb);
-       if (unlikely(is_multicast_ether_addr(eh->h_dest) &&
-                    ether_addr_equal(ndev->dev_addr, eh->h_source))) {
-               dev_kfree_skb_any(skb);
-               return -EFAULT;
-       }
-
        ring->stats.rx_pkts++;
        ring->stats.rx_bytes += skb->len;
 
@@ -747,25 +737,37 @@ static void hns_nic_rx_up_pro(struct hns_nic_ring_data *ring_data,
        ndev->last_rx = jiffies;
 }
 
+static int hns_desc_unused(struct hnae_ring *ring)
+{
+       int ntc = ring->next_to_clean;
+       int ntu = ring->next_to_use;
+
+       return ((ntc >= ntu) ? 0 : ring->desc_num) + ntc - ntu;
+}
+
 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;
+       int num, bnum;
 #define RCB_NOF_ALLOC_RX_BUFF_ONCE 16
        int recv_pkts, recv_bds, clean_count, err;
+       int unused_count = hns_desc_unused(ring);
 
        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:
+       num -= unused_count;
+
        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);
+               if (clean_count + unused_count >= RCB_NOF_ALLOC_RX_BUFF_ONCE) {
+                       hns_nic_alloc_rx_buffers(ring_data,
+                                                clean_count + unused_count);
                        clean_count = 0;
+                       unused_count = hns_desc_unused(ring);
                }
 
                /* poll one pkt */
@@ -786,21 +788,11 @@ recv:
                recv_pkts++;
        }
 
-       /* make all data has been write before submit */
-       if (recv_pkts < budget) {
-               ex_num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
-
-               if (ex_num > clean_count) {
-                       num += ex_num - clean_count;
-                       rmb(); /*complete read rx ring bd number*/
-                       goto recv;
-               }
-       }
-
 out:
        /* make all data has been write before submit */
-       if (clean_count > 0)
-               hns_nic_alloc_rx_buffers(ring_data, clean_count);
+       if (clean_count + unused_count > 0)
+               hns_nic_alloc_rx_buffers(ring_data,
+                                        clean_count + unused_count);
 
        return recv_pkts;
 }
@@ -810,6 +802,8 @@ static void hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data)
        struct hnae_ring *ring = ring_data->ring;
        int num = 0;
 
+       ring_data->ring->q->handle->dev->ops->toggle_ring_irq(ring, 0);
+
        /* for hardware bug fixed */
        num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
 
@@ -821,6 +815,20 @@ static void hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data)
        }
 }
 
+static void hns_nic_rx_fini_pro_v2(struct hns_nic_ring_data *ring_data)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       int num = 0;
+
+       num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
+
+       if (num == 0)
+               ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
+                       ring, 0);
+       else
+               napi_schedule(&ring_data->napi);
+}
+
 static inline void hns_nic_reclaim_one_desc(struct hnae_ring *ring,
                                            int *bytes, int *pkts)
 {
@@ -922,7 +930,11 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data,
 static void hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data)
 {
        struct hnae_ring *ring = ring_data->ring;
-       int head = readl_relaxed(ring->io_base + RCB_REG_HEAD);
+       int head;
+
+       ring_data->ring->q->handle->dev->ops->toggle_ring_irq(ring, 0);
+
+       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(
@@ -932,6 +944,18 @@ static void hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data)
        }
 }
 
+static void hns_nic_tx_fini_pro_v2(struct hns_nic_ring_data *ring_data)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       int 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, 0);
+       else
+               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;
@@ -963,10 +987,7 @@ static int hns_nic_common_poll(struct napi_struct *napi, int budget)
 
        if (clean_complete >= 0 && clean_complete < budget) {
                napi_complete(napi);
-               ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
-                       ring_data->ring, 0);
-               if (ring_data->fini_process)
-                       ring_data->fini_process(ring_data);
+               ring_data->fini_process(ring_data);
                return 0;
        }
 
@@ -1562,6 +1583,21 @@ struct rtnl_link_stats64 *hns_nic_get_stats64(struct net_device *ndev,
        return stats;
 }
 
+static u16
+hns_nic_select_queue(struct net_device *ndev, struct sk_buff *skb,
+                    void *accel_priv, select_queue_fallback_t fallback)
+{
+       struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+
+       /* fix hardware broadcast/multicast packets queue loopback */
+       if (!AE_IS_VER1(priv->enet_ver) &&
+           is_multicast_ether_addr(eth_hdr->h_dest))
+               return 0;
+       else
+               return fallback(ndev, skb);
+}
+
 static const struct net_device_ops hns_nic_netdev_ops = {
        .ndo_open = hns_nic_net_open,
        .ndo_stop = hns_nic_net_stop,
@@ -1577,6 +1613,7 @@ static const struct net_device_ops hns_nic_netdev_ops = {
        .ndo_poll_controller = hns_nic_poll_controller,
 #endif
        .ndo_set_rx_mode = hns_nic_set_rx_mode,
+       .ndo_select_queue = hns_nic_select_queue,
 };
 
 static void hns_nic_update_link_status(struct net_device *netdev)
@@ -1738,7 +1775,8 @@ static int hns_nic_init_ring_data(struct hns_nic_priv *priv)
                rd->queue_index = i;
                rd->ring = &h->qs[i]->tx_ring;
                rd->poll_one = hns_nic_tx_poll_one;
-               rd->fini_process = is_ver1 ? hns_nic_tx_fini_pro : NULL;
+               rd->fini_process = is_ver1 ? hns_nic_tx_fini_pro :
+                       hns_nic_tx_fini_pro_v2;
 
                netif_napi_add(priv->netdev, &rd->napi,
                               hns_nic_common_poll, NIC_TX_CLEAN_MAX_NUM);
@@ -1750,7 +1788,8 @@ static int hns_nic_init_ring_data(struct hns_nic_priv *priv)
                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 = is_ver1 ? hns_nic_rx_fini_pro : NULL;
+               rd->fini_process = is_ver1 ? hns_nic_rx_fini_pro :
+                       hns_nic_rx_fini_pro_v2;
 
                netif_napi_add(priv->netdev, &rd->napi,
                               hns_nic_common_poll, NIC_RX_CLEAN_MAX_NUM);
index 47e59bbfd061bfe1d9576b8111f92d361be599e1..87d5c94b2810230b334ddef0501644cd291b8bbf 100644 (file)
@@ -352,6 +352,13 @@ static int __lb_setup(struct net_device *ndev,
                break;
        }
 
+       if (!ret) {
+               if (loop == MAC_LOOP_NONE)
+                       h->dev->ops->set_promisc_mode(
+                               h, ndev->flags & IFF_PROMISC);
+               else
+                       h->dev->ops->set_promisc_mode(h, 1);
+       }
        return ret;
 }
 
index d4585154151db43ce1df6ef97312a41296bd39d9..cc4fd61914d30b567d962f24f15bfea3bb1da92c 100644 (file)
@@ -287,7 +287,7 @@ retry:
 
                        goto retry;
                }
-               MLX5_SET64(manage_pages_in, in, pas[i], addr);
+               MLX5_ARRAY_SET64(manage_pages_in, in, pas, i, addr);
        }
 
        MLX5_SET(manage_pages_in, in, opcode, MLX5_CMD_OP_MANAGE_PAGES);
@@ -344,7 +344,7 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev,
                if (fwp->func_id != func_id)
                        continue;
 
-               MLX5_SET64(manage_pages_out, out, pas[i], fwp->addr);
+               MLX5_ARRAY_SET64(manage_pages_out, out, pas, i, fwp->addr);
                i++;
        }
 
index 0df1391f9663b257a170e5bbf6503268207b3d2d..1e8339a67f6e1d049fc60fe340715fd663742009 100644 (file)
@@ -107,15 +107,4 @@ config QEDE
        ---help---
          This enables the support for ...
 
-config INFINIBAND_QEDR
-       tristate "QLogic qede RoCE sources [debug]"
-       depends on QEDE && 64BIT
-       select QED_LL2
-       default n
-       ---help---
-         This provides a temporary node that allows the compilation
-         and logical testing of the InfiniBand over Ethernet support
-         for QLogic QED. This would be replaced by the 'real' option
-         once the QEDR driver is added [+relocated].
-
 endif # NET_VENDOR_QLOGIC
index a6db10717d5c27a5931b2db682ecb3cb3a25cf2c..02a8be2faed7fd9fdb7c66442d613f2d7c61e565 100644 (file)
@@ -1517,7 +1517,7 @@ static void qed_ll2_register_cb_ops(struct qed_dev *cdev,
 static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params)
 {
        struct qed_ll2_info ll2_info;
-       struct qed_ll2_buffer *buffer;
+       struct qed_ll2_buffer *buffer, *tmp_buffer;
        enum qed_ll2_conn_type conn_type;
        struct qed_ptt *p_ptt;
        int rc, i;
@@ -1587,7 +1587,7 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params)
 
        /* Post all Rx buffers to FW */
        spin_lock_bh(&cdev->ll2->lock);
-       list_for_each_entry(buffer, &cdev->ll2->list, list) {
+       list_for_each_entry_safe(buffer, tmp_buffer, &cdev->ll2->list, list) {
                rc = qed_ll2_post_rx_buffer(QED_LEADING_HWFN(cdev),
                                            cdev->ll2->handle,
                                            buffer->phys_addr, 0, buffer, 1);
index 23430059471ca11da1cdc3e47bc9736143aa5bf7..76831a398bedf024d299db1ffb68c1149664ace1 100644 (file)
@@ -2947,7 +2947,7 @@ static const struct qed_rdma_ops qed_rdma_ops_pass = {
        .roce_ll2_stats = &qed_roce_ll2_stats,
 };
 
-const struct qed_rdma_ops *qed_get_rdma_ops()
+const struct qed_rdma_ops *qed_get_rdma_ops(void)
 {
        return &qed_rdma_ops_pass;
 }
index 4c8c60af798595e198a24951bf9547aa0de522dc..6c85b61aaa0bcd94230cb1766d3558a33808738e 100644 (file)
@@ -650,20 +650,27 @@ static int stmmac_init_ptp(struct stmmac_priv *priv)
        if (IS_ERR(priv->clk_ptp_ref)) {
                priv->clk_ptp_rate = clk_get_rate(priv->stmmac_clk);
                priv->clk_ptp_ref = NULL;
+               netdev_dbg(priv->dev, "PTP uses main clock\n");
        } else {
                clk_prepare_enable(priv->clk_ptp_ref);
                priv->clk_ptp_rate = clk_get_rate(priv->clk_ptp_ref);
+               netdev_dbg(priv->dev, "PTP rate %d\n", priv->clk_ptp_rate);
        }
 
        priv->adv_ts = 0;
-       if (priv->dma_cap.atime_stamp && priv->extend_desc)
+       /* Check if adv_ts can be enabled for dwmac 4.x core */
+       if (priv->plat->has_gmac4 && priv->dma_cap.atime_stamp)
+               priv->adv_ts = 1;
+       /* Dwmac 3.x core with extend_desc can support adv_ts */
+       else if (priv->extend_desc && priv->dma_cap.atime_stamp)
                priv->adv_ts = 1;
 
-       if (netif_msg_hw(priv) && priv->dma_cap.time_stamp)
-               pr_debug("IEEE 1588-2002 Time Stamp supported\n");
+       if (priv->dma_cap.time_stamp)
+               netdev_info(priv->dev, "IEEE 1588-2002 Timestamp supported\n");
 
-       if (netif_msg_hw(priv) && priv->adv_ts)
-               pr_debug("IEEE 1588-2008 Advanced Time Stamp supported\n");
+       if (priv->adv_ts)
+               netdev_info(priv->dev,
+                           "IEEE 1588-2008 Advanced Timestamp supported\n");
 
        priv->hw->ptp = &stmmac_ptp;
        priv->hwts_tx_en = 0;
@@ -1702,8 +1709,8 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
 
        if (init_ptp) {
                ret = stmmac_init_ptp(priv);
-               if (ret && ret != -EOPNOTSUPP)
-                       pr_warn("%s: failed PTP initialisation\n", __func__);
+               if (ret)
+                       netdev_warn(priv->dev, "PTP support cannot init.\n");
        }
 
 #ifdef CONFIG_DEBUG_FS
index 6e3b82972ce87e08eea493f4f7a0831e9f080849..289d52725a6c172dc70a6f2db1255afc7161b2cd 100644 (file)
@@ -186,10 +186,12 @@ int stmmac_ptp_register(struct stmmac_priv *priv)
                                             priv->device);
        if (IS_ERR(priv->ptp_clock)) {
                priv->ptp_clock = NULL;
-               pr_err("ptp_clock_register() failed on %s\n", priv->dev->name);
-       } else if (priv->ptp_clock)
-               pr_debug("Added PTP HW clock successfully on %s\n",
-                        priv->dev->name);
+               return PTR_ERR(priv->ptp_clock);
+       }
+
+       spin_lock_init(&priv->ptp_lock);
+
+       netdev_dbg(priv->dev, "Added PTP HW clock successfully\n");
 
        return 0;
 }
index ece0ea0f6b38b40eb2e2ece7b00b60128b328df9..6c7ec1ddd475afd2e30254b3b159166b01f8eb0e 100644 (file)
@@ -610,8 +610,8 @@ err_out_regions:
 #ifdef CONFIG_PCI
        if (pdev)
                pci_release_regions(pdev);
-#endif
 err_out:
+#endif
        if (pdev)
                pci_disable_device(pdev);
        return rc;
index 35f9f9742a48890989a3f7e04e478eb0c7b1d8cb..c688d68c39aaa75c832b2c4bb949f2676dd23aa8 100644 (file)
@@ -431,8 +431,7 @@ static void axienet_setoptions(struct net_device *ndev, u32 options)
        lp->options |= options;
 }
 
-static void __axienet_device_reset(struct axienet_local *lp,
-                                  struct device *dev, off_t offset)
+static void __axienet_device_reset(struct axienet_local *lp, off_t offset)
 {
        u32 timeout;
        /* Reset Axi DMA. This would reset Axi Ethernet core as well. The reset
@@ -468,8 +467,8 @@ static void axienet_device_reset(struct net_device *ndev)
        u32 axienet_status;
        struct axienet_local *lp = netdev_priv(ndev);
 
-       __axienet_device_reset(lp, &ndev->dev, XAXIDMA_TX_CR_OFFSET);
-       __axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET);
+       __axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET);
+       __axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET);
 
        lp->max_frm_size = XAE_MAX_VLAN_FRAME_SIZE;
        lp->options |= XAE_OPTION_VLAN;
@@ -1338,8 +1337,8 @@ static void axienet_dma_err_handler(unsigned long data)
        axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg &
                    ~XAE_MDIO_MC_MDIOEN_MASK));
 
-       __axienet_device_reset(lp, &ndev->dev, XAXIDMA_TX_CR_OFFSET);
-       __axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET);
+       __axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET);
+       __axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET);
 
        axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg);
        axienet_mdio_wait_until_ready(lp);
index 52eeb2f672766fc583849c97209564fb53fd0d5f..f0919bd3a56324c2c9f37b5f69c5132642b78f3b 100644 (file)
@@ -442,8 +442,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        }
 
        net_trans_info = get_net_transport_info(skb, &hdr_offset);
-       if (net_trans_info == TRANSPORT_INFO_NOT_IP)
-               goto do_send;
 
        /*
         * Setup the sendside checksum offload only if this is not a
@@ -478,56 +476,29 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                }
                lso_info->lso_v2_transmit.tcp_header_offset = hdr_offset;
                lso_info->lso_v2_transmit.mss = skb_shinfo(skb)->gso_size;
-               goto do_send;
-       }
-
-       if ((skb->ip_summed == CHECKSUM_NONE) ||
-           (skb->ip_summed == CHECKSUM_UNNECESSARY))
-               goto do_send;
-
-       rndis_msg_size += NDIS_CSUM_PPI_SIZE;
-       ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE,
-                           TCPIP_CHKSUM_PKTINFO);
-
-       csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi +
-                       ppi->ppi_offset);
-
-       if (net_trans_info & (INFO_IPV4 << 16))
-               csum_info->transmit.is_ipv4 = 1;
-       else
-               csum_info->transmit.is_ipv6 = 1;
-
-       if (net_trans_info & INFO_TCP) {
-               csum_info->transmit.tcp_checksum = 1;
-               csum_info->transmit.tcp_header_offset = hdr_offset;
-       } else if (net_trans_info & INFO_UDP) {
-               /* UDP checksum offload is not supported on ws2008r2.
-                * Furthermore, on ws2012 and ws2012r2, there are some
-                * issues with udp checksum offload from Linux guests.
-                * (these are host issues).
-                * For now compute the checksum here.
-                */
-               struct udphdr *uh;
-               u16 udp_len;
-
-               ret = skb_cow_head(skb, 0);
-               if (ret)
-                       goto no_memory;
-
-               uh = udp_hdr(skb);
-               udp_len = ntohs(uh->len);
-               uh->check = 0;
-               uh->check = csum_tcpudp_magic(ip_hdr(skb)->saddr,
-                                             ip_hdr(skb)->daddr,
-                                             udp_len, IPPROTO_UDP,
-                                             csum_partial(uh, udp_len, 0));
-               if (uh->check == 0)
-                       uh->check = CSUM_MANGLED_0;
-
-               csum_info->transmit.udp_checksum = 0;
+       } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               if (net_trans_info & INFO_TCP) {
+                       rndis_msg_size += NDIS_CSUM_PPI_SIZE;
+                       ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE,
+                                           TCPIP_CHKSUM_PKTINFO);
+
+                       csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi +
+                                                                        ppi->ppi_offset);
+
+                       if (net_trans_info & (INFO_IPV4 << 16))
+                               csum_info->transmit.is_ipv4 = 1;
+                       else
+                               csum_info->transmit.is_ipv6 = 1;
+
+                       csum_info->transmit.tcp_checksum = 1;
+                       csum_info->transmit.tcp_header_offset = hdr_offset;
+               } else {
+                       /* UDP checksum (and other) offload is not supported. */
+                       if (skb_checksum_help(skb))
+                               goto drop;
+               }
        }
 
-do_send:
        /* Start filling in the page buffers with the rndis hdr */
        rndis_msg->msg_len += rndis_msg_size;
        packet->total_data_buflen = rndis_msg->msg_len;
index c6f66832a1a641e387fe093b4e6aa61115e6abfa..f424b867f73e0fb4675c16e565cdeab0af8af0c8 100644 (file)
@@ -607,6 +607,21 @@ void phy_start_machine(struct phy_device *phydev)
        queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);
 }
 
+/**
+ * phy_trigger_machine - trigger the state machine to run
+ *
+ * @phydev: the phy_device struct
+ *
+ * Description: There has been a change in state which requires that the
+ *   state machine runs.
+ */
+
+static void phy_trigger_machine(struct phy_device *phydev)
+{
+       cancel_delayed_work_sync(&phydev->state_queue);
+       queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0);
+}
+
 /**
  * phy_stop_machine - stop the PHY state machine tracking
  * @phydev: target phy_device struct
@@ -639,6 +654,8 @@ static void phy_error(struct phy_device *phydev)
        mutex_lock(&phydev->lock);
        phydev->state = PHY_HALTED;
        mutex_unlock(&phydev->lock);
+
+       phy_trigger_machine(phydev);
 }
 
 /**
@@ -800,8 +817,7 @@ void phy_change(struct work_struct *work)
        }
 
        /* reschedule state queue work to run as soon as possible */
-       cancel_delayed_work_sync(&phydev->state_queue);
-       queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0);
+       phy_trigger_machine(phydev);
        return;
 
 ignore:
@@ -890,6 +906,8 @@ void phy_start(struct phy_device *phydev)
        /* if phy was suspended, bring the physical link up again */
        if (do_resume)
                phy_resume(phydev);
+
+       phy_trigger_machine(phydev);
 }
 EXPORT_SYMBOL(phy_start);
 
index 9d1fce8a6e84c5e7ce4c19ce73d31bf404305298..3ff76c6db4f6d7d8bf7e5baee03cdbcf38976d76 100644 (file)
@@ -59,6 +59,10 @@ enum qmi_wwan_flags {
        QMI_WWAN_FLAG_RAWIP = 1 << 0,
 };
 
+enum qmi_wwan_quirks {
+       QMI_WWAN_QUIRK_DTR = 1 << 0,    /* needs "set DTR" request */
+};
+
 static void qmi_wwan_netdev_setup(struct net_device *net)
 {
        struct usbnet *dev = netdev_priv(net);
@@ -411,9 +415,14 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
         * clearing out state the clients might need.
         *
         * MDM9x30 is the first QMI chipset with USB3 support. Abuse
-        * this fact to enable the quirk.
+        * this fact to enable the quirk for all USB3 devices.
+        *
+        * There are also chipsets with the same "set DTR" requirement
+        * but without USB3 support.  Devices based on these chips
+        * need a quirk flag in the device ID table.
         */
-       if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
+       if (dev->driver_info->data & QMI_WWAN_QUIRK_DTR ||
+           le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
                qmi_wwan_manage_power(dev, 1);
                qmi_wwan_change_dtr(dev, true);
        }
@@ -526,6 +535,16 @@ static const struct driver_info    qmi_wwan_info = {
        .rx_fixup       = qmi_wwan_rx_fixup,
 };
 
+static const struct driver_info        qmi_wwan_info_quirk_dtr = {
+       .description    = "WWAN/QMI device",
+       .flags          = FLAG_WWAN,
+       .bind           = qmi_wwan_bind,
+       .unbind         = qmi_wwan_unbind,
+       .manage_power   = qmi_wwan_manage_power,
+       .rx_fixup       = qmi_wwan_rx_fixup,
+       .data           = QMI_WWAN_QUIRK_DTR,
+};
+
 #define HUAWEI_VENDOR_ID       0x12D1
 
 /* map QMI/wwan function by a fixed interface number */
@@ -533,6 +552,11 @@ static const struct driver_info    qmi_wwan_info = {
        USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
        .driver_info = (unsigned long)&qmi_wwan_info
 
+/* devices requiring "set DTR" quirk */
+#define QMI_QUIRK_SET_DTR(vend, prod, num) \
+       USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
+       .driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr
+
 /* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */
 #define QMI_GOBI1K_DEVICE(vend, prod) \
        QMI_FIXED_INTF(vend, prod, 3)
@@ -895,6 +919,8 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)},    /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
        {QMI_FIXED_INTF(0x22de, 0x9061, 3)},    /* WeTelecom WPD-600N */
        {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)},    /* SIMCom 7230E */
+       {QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0  Mini PCIe */
+       {QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */
 
        /* 4. Gobi 1000 devices */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},    /* Acer Gobi Modem Device */
index cf68149cbb558200909fa2920eaa0cd7ba9e219d..3ce1f7da864742a2aa1fe268ffd440b09764ea05 100644 (file)
@@ -407,4 +407,8 @@ u32 xenvif_set_hash_mapping(struct xenvif *vif, u32 gref, u32 len,
 
 void xenvif_set_skb_hash(struct xenvif *vif, struct sk_buff *skb);
 
+#ifdef CONFIG_DEBUG_FS
+void xenvif_dump_hash_info(struct xenvif *vif, struct seq_file *m);
+#endif
+
 #endif /* __XEN_NETBACK__COMMON_H__ */
index 613bac057650a14a759ec8feecfbae53ab195498..e8c5dddc54ba27ee43354ab1fae263e0c31c5f1e 100644 (file)
@@ -360,6 +360,74 @@ u32 xenvif_set_hash_mapping(struct xenvif *vif, u32 gref, u32 len,
        return XEN_NETIF_CTRL_STATUS_SUCCESS;
 }
 
+#ifdef CONFIG_DEBUG_FS
+void xenvif_dump_hash_info(struct xenvif *vif, struct seq_file *m)
+{
+       unsigned int i;
+
+       switch (vif->hash.alg) {
+       case XEN_NETIF_CTRL_HASH_ALGORITHM_TOEPLITZ:
+               seq_puts(m, "Hash Algorithm: TOEPLITZ\n");
+               break;
+
+       case XEN_NETIF_CTRL_HASH_ALGORITHM_NONE:
+               seq_puts(m, "Hash Algorithm: NONE\n");
+               /* FALLTHRU */
+       default:
+               return;
+       }
+
+       if (vif->hash.flags) {
+               seq_puts(m, "\nHash Flags:\n");
+
+               if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4)
+                       seq_puts(m, "- IPv4\n");
+               if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP)
+                       seq_puts(m, "- IPv4 + TCP\n");
+               if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6)
+                       seq_puts(m, "- IPv6\n");
+               if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP)
+                       seq_puts(m, "- IPv6 + TCP\n");
+       }
+
+       seq_puts(m, "\nHash Key:\n");
+
+       for (i = 0; i < XEN_NETBK_MAX_HASH_KEY_SIZE; ) {
+               unsigned int j, n;
+
+               n = 8;
+               if (i + n >= XEN_NETBK_MAX_HASH_KEY_SIZE)
+                       n = XEN_NETBK_MAX_HASH_KEY_SIZE - i;
+
+               seq_printf(m, "[%2u - %2u]: ", i, i + n - 1);
+
+               for (j = 0; j < n; j++, i++)
+                       seq_printf(m, "%02x ", vif->hash.key[i]);
+
+               seq_puts(m, "\n");
+       }
+
+       if (vif->hash.size != 0) {
+               seq_puts(m, "\nHash Mapping:\n");
+
+               for (i = 0; i < vif->hash.size; ) {
+                       unsigned int j, n;
+
+                       n = 8;
+                       if (i + n >= vif->hash.size)
+                               n = vif->hash.size - i;
+
+                       seq_printf(m, "[%4u - %4u]: ", i, i + n - 1);
+
+                       for (j = 0; j < n; j++, i++)
+                               seq_printf(m, "%4u ", vif->hash.mapping[i]);
+
+                       seq_puts(m, "\n");
+               }
+       }
+}
+#endif /* CONFIG_DEBUG_FS */
+
 void xenvif_init_hash(struct xenvif *vif)
 {
        if (xenvif_hash_cache_size == 0)
index 8e9ade6ccf18ae0055c105e09ca81253db0611cc..b1cf7c6f407a9ecf45bfaac50695fd6c691c158d 100644 (file)
@@ -337,9 +337,9 @@ static void xenvif_rx_next_chunk(struct xenvif_queue *queue,
        frag_data += pkt->frag_offset;
        frag_len -= pkt->frag_offset;
 
-       chunk_len = min(frag_len, XEN_PAGE_SIZE - offset);
-       chunk_len = min(chunk_len,
-                       XEN_PAGE_SIZE - xen_offset_in_page(frag_data));
+       chunk_len = min_t(size_t, frag_len, XEN_PAGE_SIZE - offset);
+       chunk_len = min_t(size_t, chunk_len, XEN_PAGE_SIZE -
+                                            xen_offset_in_page(frag_data));
 
        pkt->frag_offset += chunk_len;
 
@@ -425,6 +425,8 @@ void xenvif_rx_skb(struct xenvif_queue *queue)
 
        xenvif_rx_next_skb(queue, &pkt);
 
+       queue->last_rx_time = jiffies;
+
        do {
                struct xen_netif_rx_request *req;
                struct xen_netif_rx_response *rsp;
index 7056404e3cb89e712038b3076e92e3692a8fb8cf..8674e188b697d91741cb8e5eaef96918eb50224d 100644 (file)
@@ -165,7 +165,7 @@ xenvif_write_io_ring(struct file *filp, const char __user *buf, size_t count,
        return count;
 }
 
-static int xenvif_dump_open(struct inode *inode, struct file *filp)
+static int xenvif_io_ring_open(struct inode *inode, struct file *filp)
 {
        int ret;
        void *queue = NULL;
@@ -179,13 +179,35 @@ static int xenvif_dump_open(struct inode *inode, struct file *filp)
 
 static const struct file_operations xenvif_dbg_io_ring_ops_fops = {
        .owner = THIS_MODULE,
-       .open = xenvif_dump_open,
+       .open = xenvif_io_ring_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .write = xenvif_write_io_ring,
 };
 
+static int xenvif_read_ctrl(struct seq_file *m, void *v)
+{
+       struct xenvif *vif = m->private;
+
+       xenvif_dump_hash_info(vif, m);
+
+       return 0;
+}
+
+static int xenvif_ctrl_open(struct inode *inode, struct file *filp)
+{
+       return single_open(filp, xenvif_read_ctrl, inode->i_private);
+}
+
+static const struct file_operations xenvif_dbg_ctrl_ops_fops = {
+       .owner = THIS_MODULE,
+       .open = xenvif_ctrl_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
 static void xenvif_debugfs_addif(struct xenvif *vif)
 {
        struct dentry *pfile;
@@ -210,6 +232,17 @@ static void xenvif_debugfs_addif(struct xenvif *vif)
                                pr_warn("Creation of io_ring file returned %ld!\n",
                                        PTR_ERR(pfile));
                }
+
+               if (vif->ctrl_irq) {
+                       pfile = debugfs_create_file("ctrl",
+                                                   S_IRUSR,
+                                                   vif->xenvif_dbg_root,
+                                                   vif,
+                                                   &xenvif_dbg_ctrl_ops_fops);
+                       if (IS_ERR_OR_NULL(pfile))
+                               pr_warn("Creation of ctrl file returned %ld!\n",
+                                       PTR_ERR(pfile));
+               }
        } else
                netdev_warn(vif->dev,
                            "Creation of vif debugfs dir returned %ld!\n",
index f811d27964370475040b3a51b3b86d590b621cf8..e4bf07d20f9bbf416f756ca381d3340ce662272f 100644 (file)
@@ -29,6 +29,7 @@
 const struct of_device_id of_default_bus_match_table[] = {
        { .compatible = "simple-bus", },
        { .compatible = "simple-mfd", },
+       { .compatible = "isa", },
 #ifdef CONFIG_ARM_AMBA
        { .compatible = "arm,amba-bus", },
 #endif /* CONFIG_ARM_AMBA */
index e4a5b7ee90cfcb2226b256064c46d5ef2665f86a..4fce494271cc60387c08d5675caa474047727854 100644 (file)
@@ -230,20 +230,20 @@ static int advk_pcie_link_up(struct advk_pcie *pcie)
 
 static int advk_pcie_wait_for_link(struct advk_pcie *pcie)
 {
+       struct device *dev = &pcie->pdev->dev;
        int retries;
 
        /* check if the link is up or not */
        for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
                if (advk_pcie_link_up(pcie)) {
-                       dev_info(&pcie->pdev->dev, "link up\n");
+                       dev_info(dev, "link up\n");
                        return 0;
                }
 
                usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
        }
 
-       dev_err(&pcie->pdev->dev, "link never came up\n");
-
+       dev_err(dev, "link never came up\n");
        return -ETIMEDOUT;
 }
 
@@ -376,6 +376,7 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
 
 static void advk_pcie_check_pio_status(struct advk_pcie *pcie)
 {
+       struct device *dev = &pcie->pdev->dev;
        u32 reg;
        unsigned int status;
        char *strcomp_status, *str_posted;
@@ -407,12 +408,13 @@ static void advk_pcie_check_pio_status(struct advk_pcie *pcie)
        else
                str_posted = "Posted";
 
-       dev_err(&pcie->pdev->dev, "%s PIO Response Status: %s, %#x @ %#x\n",
+       dev_err(dev, "%s PIO Response Status: %s, %#x @ %#x\n",
                str_posted, strcomp_status, reg, advk_readl(pcie, PIO_ADDR_LS));
 }
 
 static int advk_pcie_wait_pio(struct advk_pcie *pcie)
 {
+       struct device *dev = &pcie->pdev->dev;
        unsigned long timeout;
 
        timeout = jiffies + msecs_to_jiffies(PIO_TIMEOUT_MS);
@@ -426,7 +428,7 @@ static int advk_pcie_wait_pio(struct advk_pcie *pcie)
                        return 0;
        }
 
-       dev_err(&pcie->pdev->dev, "config read/write timed out\n");
+       dev_err(dev, "config read/write timed out\n");
        return -ETIMEDOUT;
 }
 
@@ -560,10 +562,11 @@ static int advk_pcie_alloc_msi(struct advk_pcie *pcie)
 
 static void advk_pcie_free_msi(struct advk_pcie *pcie, int hwirq)
 {
+       struct device *dev = &pcie->pdev->dev;
+
        mutex_lock(&pcie->msi_used_lock);
        if (!test_bit(hwirq, pcie->msi_irq_in_use))
-               dev_err(&pcie->pdev->dev, "trying to free unused MSI#%d\n",
-                       hwirq);
+               dev_err(dev, "trying to free unused MSI#%d\n", hwirq);
        else
                clear_bit(hwirq, pcie->msi_irq_in_use);
        mutex_unlock(&pcie->msi_used_lock);
@@ -910,6 +913,7 @@ out_release_res:
 
 static int advk_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct advk_pcie *pcie;
        struct resource *res;
        struct pci_bus *bus, *child;
@@ -917,31 +921,29 @@ static int advk_pcie_probe(struct platform_device *pdev)
        struct device_node *msi_node;
        int ret, irq;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(struct advk_pcie),
-                           GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(struct advk_pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
        pcie->pdev = pdev;
-       platform_set_drvdata(pdev, pcie);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       pcie->base = devm_ioremap_resource(&pdev->dev, res);
+       pcie->base = devm_ioremap_resource(dev, res);
        if (IS_ERR(pcie->base))
                return PTR_ERR(pcie->base);
 
        irq = platform_get_irq(pdev, 0);
-       ret = devm_request_irq(&pdev->dev, irq, advk_pcie_irq_handler,
+       ret = devm_request_irq(dev, irq, advk_pcie_irq_handler,
                               IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie",
                               pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed to register interrupt\n");
+               dev_err(dev, "Failed to register interrupt\n");
                return ret;
        }
 
        ret = advk_pcie_parse_request_of_pci_ranges(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed to parse resources\n");
+               dev_err(dev, "Failed to parse resources\n");
                return ret;
        }
 
@@ -949,24 +951,24 @@ static int advk_pcie_probe(struct platform_device *pdev)
 
        ret = advk_pcie_init_irq_domain(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed to initialize irq\n");
+               dev_err(dev, "Failed to initialize irq\n");
                return ret;
        }
 
        ret = advk_pcie_init_msi_irq_domain(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed to initialize irq\n");
+               dev_err(dev, "Failed to initialize irq\n");
                advk_pcie_remove_irq_domain(pcie);
                return ret;
        }
 
-       msi_node = of_parse_phandle(pdev->dev.of_node, "msi-parent", 0);
+       msi_node = of_parse_phandle(dev->of_node, "msi-parent", 0);
        if (msi_node)
                msi = of_pci_find_msi_chip_by_node(msi_node);
        else
                msi = NULL;
 
-       bus = pci_scan_root_bus_msi(&pdev->dev, 0, &advk_pcie_ops,
+       bus = pci_scan_root_bus_msi(dev, 0, &advk_pcie_ops,
                                    pcie, &pcie->resources, &pcie->msi);
        if (!bus) {
                advk_pcie_remove_msi_irq_domain(pcie);
@@ -980,7 +982,6 @@ static int advk_pcie_probe(struct platform_device *pdev)
                pcie_bus_configure_settings(child);
 
        pci_bus_add_devices(bus);
-
        return 0;
 }
 
index 19223ed2e619f6e7f622ef6b1df8655353105747..9595fad63f6f233c35906ea77fc620085e086154 100644 (file)
 #define        DRA7XX_CPU_TO_BUS_ADDR                          0x0FFFFFFF
 
 struct dra7xx_pcie {
-       void __iomem            *base;
-       struct phy              **phy;
-       int                     phy_count;
-       struct device           *dev;
        struct pcie_port        pp;
+       void __iomem            *base;          /* DT ti_conf */
+       int                     phy_count;      /* DT phy-names count */
+       struct phy              **phy;
 };
 
 #define to_dra7xx_pcie(x)      container_of((x), struct dra7xx_pcie, pp)
@@ -84,17 +83,6 @@ static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset,
        writel(value, pcie->base + offset);
 }
 
-static inline u32 dra7xx_pcie_readl_rc(struct pcie_port *pp, u32 offset)
-{
-       return readl(pp->dbi_base + offset);
-}
-
-static inline void dra7xx_pcie_writel_rc(struct pcie_port *pp, u32 offset,
-                                        u32 value)
-{
-       writel(value, pp->dbi_base + offset);
-}
-
 static int dra7xx_pcie_link_up(struct pcie_port *pp)
 {
        struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
@@ -103,13 +91,14 @@ static int dra7xx_pcie_link_up(struct pcie_port *pp)
        return !!(reg & LINK_UP);
 }
 
-static int dra7xx_pcie_establish_link(struct pcie_port *pp)
+static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx)
 {
-       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
+       struct pcie_port *pp = &dra7xx->pp;
+       struct device *dev = pp->dev;
        u32 reg;
 
        if (dw_pcie_link_up(pp)) {
-               dev_err(pp->dev, "link is already up\n");
+               dev_err(dev, "link is already up\n");
                return 0;
        }
 
@@ -120,10 +109,8 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
        return dw_pcie_wait_for_link(pp);
 }
 
-static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp)
+static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx)
 {
-       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
-
        dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN,
                           ~INTERRUPTS);
        dra7xx_pcie_writel(dra7xx,
@@ -142,6 +129,8 @@ static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp)
 
 static void dra7xx_pcie_host_init(struct pcie_port *pp)
 {
+       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
+
        pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR;
        pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR;
        pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR;
@@ -149,10 +138,10 @@ static void dra7xx_pcie_host_init(struct pcie_port *pp)
 
        dw_pcie_setup_rc(pp);
 
-       dra7xx_pcie_establish_link(pp);
+       dra7xx_pcie_establish_link(dra7xx);
        if (IS_ENABLED(CONFIG_PCI_MSI))
                dw_pcie_msi_init(pp);
-       dra7xx_pcie_enable_interrupts(pp);
+       dra7xx_pcie_enable_interrupts(dra7xx);
 }
 
 static struct pcie_host_ops dra7xx_pcie_host_ops = {
@@ -196,8 +185,8 @@ static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp)
 
 static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
-       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
+       struct dra7xx_pcie *dra7xx = arg;
+       struct pcie_port *pp = &dra7xx->pp;
        u32 reg;
 
        reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI);
@@ -223,51 +212,51 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
 static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
 {
        struct dra7xx_pcie *dra7xx = arg;
+       struct device *dev = dra7xx->pp.dev;
        u32 reg;
 
        reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN);
 
        if (reg & ERR_SYS)
-               dev_dbg(dra7xx->dev, "System Error\n");
+               dev_dbg(dev, "System Error\n");
 
        if (reg & ERR_FATAL)
-               dev_dbg(dra7xx->dev, "Fatal Error\n");
+               dev_dbg(dev, "Fatal Error\n");
 
        if (reg & ERR_NONFATAL)
-               dev_dbg(dra7xx->dev, "Non Fatal Error\n");
+               dev_dbg(dev, "Non Fatal Error\n");
 
        if (reg & ERR_COR)
-               dev_dbg(dra7xx->dev, "Correctable Error\n");
+               dev_dbg(dev, "Correctable Error\n");
 
        if (reg & ERR_AXI)
-               dev_dbg(dra7xx->dev, "AXI tag lookup fatal Error\n");
+               dev_dbg(dev, "AXI tag lookup fatal Error\n");
 
        if (reg & ERR_ECRC)
-               dev_dbg(dra7xx->dev, "ECRC Error\n");
+               dev_dbg(dev, "ECRC Error\n");
 
        if (reg & PME_TURN_OFF)
-               dev_dbg(dra7xx->dev,
+               dev_dbg(dev,
                        "Power Management Event Turn-Off message received\n");
 
        if (reg & PME_TO_ACK)
-               dev_dbg(dra7xx->dev,
+               dev_dbg(dev,
                        "Power Management Turn-Off Ack message received\n");
 
        if (reg & PM_PME)
-               dev_dbg(dra7xx->dev,
-                       "PM Power Management Event message received\n");
+               dev_dbg(dev, "PM Power Management Event message received\n");
 
        if (reg & LINK_REQ_RST)
-               dev_dbg(dra7xx->dev, "Link Request Reset\n");
+               dev_dbg(dev, "Link Request Reset\n");
 
        if (reg & LINK_UP_EVT)
-               dev_dbg(dra7xx->dev, "Link-up state change\n");
+               dev_dbg(dev, "Link-up state change\n");
 
        if (reg & CFG_BME_EVT)
-               dev_dbg(dra7xx->dev, "CFG 'Bus Master Enable' change\n");
+               dev_dbg(dev, "CFG 'Bus Master Enable' change\n");
 
        if (reg & CFG_MSE_EVT)
-               dev_dbg(dra7xx->dev, "CFG 'Memory Space Enable' change\n");
+               dev_dbg(dev, "CFG 'Memory Space Enable' change\n");
 
        dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg);
 
@@ -278,13 +267,9 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
                                       struct platform_device *pdev)
 {
        int ret;
-       struct pcie_port *pp;
+       struct pcie_port *pp = &dra7xx->pp;
+       struct device *dev = pp->dev;
        struct resource *res;
-       struct device *dev = &pdev->dev;
-
-       pp = &dra7xx->pp;
-       pp->dev = dev;
-       pp->ops = &dra7xx_pcie_host_ops;
 
        pp->irq = platform_get_irq(pdev, 1);
        if (pp->irq < 0) {
@@ -292,12 +277,11 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
                return -EINVAL;
        }
 
-       ret = devm_request_irq(&pdev->dev, pp->irq,
-                              dra7xx_pcie_msi_irq_handler,
+       ret = devm_request_irq(dev, pp->irq, dra7xx_pcie_msi_irq_handler,
                               IRQF_SHARED | IRQF_NO_THREAD,
-                              "dra7-pcie-msi", pp);
+                              "dra7-pcie-msi", dra7xx);
        if (ret) {
-               dev_err(&pdev->dev, "failed to request irq\n");
+               dev_err(dev, "failed to request irq\n");
                return ret;
        }
 
@@ -314,7 +298,7 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(dra7xx->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -332,6 +316,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
        void __iomem *base;
        struct resource *res;
        struct dra7xx_pcie *dra7xx;
+       struct pcie_port *pp;
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
        char name[10];
@@ -343,6 +328,10 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
        if (!dra7xx)
                return -ENOMEM;
 
+       pp = &dra7xx->pp;
+       pp->dev = dev;
+       pp->ops = &dra7xx_pcie_host_ops;
+
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(dev, "missing IRQ resource\n");
@@ -390,7 +379,6 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
 
        dra7xx->base = base;
        dra7xx->phy = phy;
-       dra7xx->dev = dev;
        dra7xx->phy_count = phy_count;
 
        pm_runtime_enable(dev);
@@ -407,7 +395,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
                ret = devm_gpio_request_one(dev, gpio_sel, gpio_flags,
                                            "pcie_reset");
                if (ret) {
-                       dev_err(&pdev->dev, "gpio%d request failed, ret %d\n",
+                       dev_err(dev, "gpio%d request failed, ret %d\n",
                                gpio_sel, ret);
                        goto err_gpio;
                }
@@ -420,12 +408,11 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
        reg &= ~LTSSM_EN;
        dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
 
-       platform_set_drvdata(pdev, dra7xx);
-
        ret = dra7xx_add_pcie_port(dra7xx, pdev);
        if (ret < 0)
                goto err_gpio;
 
+       platform_set_drvdata(pdev, dra7xx);
        return 0;
 
 err_gpio:
@@ -451,9 +438,9 @@ static int dra7xx_pcie_suspend(struct device *dev)
        u32 val;
 
        /* clear MSE */
-       val = dra7xx_pcie_readl_rc(pp, PCI_COMMAND);
+       val = dw_pcie_readl_rc(pp, PCI_COMMAND);
        val &= ~PCI_COMMAND_MEMORY;
-       dra7xx_pcie_writel_rc(pp, PCI_COMMAND, val);
+       dw_pcie_writel_rc(pp, PCI_COMMAND, val);
 
        return 0;
 }
@@ -465,9 +452,9 @@ static int dra7xx_pcie_resume(struct device *dev)
        u32 val;
 
        /* set MSE */
-       val = dra7xx_pcie_readl_rc(pp, PCI_COMMAND);
+       val = dw_pcie_readl_rc(pp, PCI_COMMAND);
        val |= PCI_COMMAND_MEMORY;
-       dra7xx_pcie_writel_rc(pp, PCI_COMMAND, val);
+       dw_pcie_writel_rc(pp, PCI_COMMAND, val);
 
        return 0;
 }
index 2e2d7f00b9e8ad0c7ed007d00d64db86f7b76f8f..f1c544bb8b684b7c969bfb574801a76810a6dca0 100644 (file)
 #define to_exynos_pcie(x)      container_of(x, struct exynos_pcie, pp)
 
 struct exynos_pcie {
-       void __iomem            *elbi_base;
-       void __iomem            *phy_base;
-       void __iomem            *block_base;
+       struct pcie_port        pp;
+       void __iomem            *elbi_base;     /* DT 0th resource */
+       void __iomem            *phy_base;      /* DT 1st resource */
+       void __iomem            *block_base;    /* DT 2nd resource */
        int                     reset_gpio;
        struct clk              *clk;
        struct clk              *bus_clk;
-       struct pcie_port        pp;
 };
 
 /* PCIe ELBI registers */
@@ -102,40 +102,40 @@ struct exynos_pcie {
 #define PCIE_PHY_TRSV3_PD_TSV          (0x1 << 7)
 #define PCIE_PHY_TRSV3_LVCC            0x31c
 
-static inline void exynos_elb_writel(struct exynos_pcie *pcie, u32 val, u32 reg)
+static void exynos_elb_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg)
 {
-       writel(val, pcie->elbi_base + reg);
+       writel(val, exynos_pcie->elbi_base + reg);
 }
 
-static inline u32 exynos_elb_readl(struct exynos_pcie *pcie, u32 reg)
+static u32 exynos_elb_readl(struct exynos_pcie *exynos_pcie, u32 reg)
 {
-       return readl(pcie->elbi_base + reg);
+       return readl(exynos_pcie->elbi_base + reg);
 }
 
-static inline void exynos_phy_writel(struct exynos_pcie *pcie, u32 val, u32 reg)
+static void exynos_phy_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg)
 {
-       writel(val, pcie->phy_base + reg);
+       writel(val, exynos_pcie->phy_base + reg);
 }
 
-static inline u32 exynos_phy_readl(struct exynos_pcie *pcie, u32 reg)
+static u32 exynos_phy_readl(struct exynos_pcie *exynos_pcie, u32 reg)
 {
-       return readl(pcie->phy_base + reg);
+       return readl(exynos_pcie->phy_base + reg);
 }
 
-static inline void exynos_blk_writel(struct exynos_pcie *pcie, u32 val, u32 reg)
+static void exynos_blk_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg)
 {
-       writel(val, pcie->block_base + reg);
+       writel(val, exynos_pcie->block_base + reg);
 }
 
-static inline u32 exynos_blk_readl(struct exynos_pcie *pcie, u32 reg)
+static u32 exynos_blk_readl(struct exynos_pcie *exynos_pcie, u32 reg)
 {
-       return readl(pcie->block_base + reg);
+       return readl(exynos_pcie->block_base + reg);
 }
 
-static void exynos_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on)
+static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *exynos_pcie,
+                                           bool on)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        if (on) {
                val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_AWMISC);
@@ -148,10 +148,10 @@ static void exynos_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on)
        }
 }
 
-static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on)
+static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *exynos_pcie,
+                                           bool on)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        if (on) {
                val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_ARMISC);
@@ -164,10 +164,9 @@ static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on)
        }
 }
 
-static void exynos_pcie_assert_core_reset(struct pcie_port *pp)
+static void exynos_pcie_assert_core_reset(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET);
        val &= ~PCIE_CORE_RESET_ENABLE;
@@ -177,10 +176,9 @@ static void exynos_pcie_assert_core_reset(struct pcie_port *pp)
        exynos_elb_writel(exynos_pcie, 0, PCIE_NONSTICKY_RESET);
 }
 
-static void exynos_pcie_deassert_core_reset(struct pcie_port *pp)
+static void exynos_pcie_deassert_core_reset(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET);
        val |= PCIE_CORE_RESET_ENABLE;
@@ -193,18 +191,14 @@ static void exynos_pcie_deassert_core_reset(struct pcie_port *pp)
        exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_MAC_RESET);
 }
 
-static void exynos_pcie_assert_phy_reset(struct pcie_port *pp)
+static void exynos_pcie_assert_phy_reset(struct exynos_pcie *exynos_pcie)
 {
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
-
        exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_MAC_RESET);
        exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_GLOBAL_RESET);
 }
 
-static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp)
+static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *exynos_pcie)
 {
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
-
        exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_GLOBAL_RESET);
        exynos_elb_writel(exynos_pcie, 1, PCIE_PWR_RESET);
        exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
@@ -213,10 +207,9 @@ static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp)
        exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET);
 }
 
-static void exynos_pcie_power_on_phy(struct pcie_port *pp)
+static void exynos_pcie_power_on_phy(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER);
        val &= ~PCIE_PHY_COMMON_PD_CMN;
@@ -239,10 +232,9 @@ static void exynos_pcie_power_on_phy(struct pcie_port *pp)
        exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER);
 }
 
-static void exynos_pcie_power_off_phy(struct pcie_port *pp)
+static void exynos_pcie_power_off_phy(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER);
        val |= PCIE_PHY_COMMON_PD_CMN;
@@ -265,10 +257,8 @@ static void exynos_pcie_power_off_phy(struct pcie_port *pp)
        exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER);
 }
 
-static void exynos_pcie_init_phy(struct pcie_port *pp)
+static void exynos_pcie_init_phy(struct exynos_pcie *exynos_pcie)
 {
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
-
        /* DCC feedback control off */
        exynos_phy_writel(exynos_pcie, 0x29, PCIE_PHY_DCC_FEEDBACK);
 
@@ -305,51 +295,41 @@ static void exynos_pcie_init_phy(struct pcie_port *pp)
        exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV3_LVCC);
 }
 
-static void exynos_pcie_assert_reset(struct pcie_port *pp)
+static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
 {
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       struct pcie_port *pp = &exynos_pcie->pp;
+       struct device *dev = pp->dev;
 
        if (exynos_pcie->reset_gpio >= 0)
-               devm_gpio_request_one(pp->dev, exynos_pcie->reset_gpio,
+               devm_gpio_request_one(dev, exynos_pcie->reset_gpio,
                                GPIOF_OUT_INIT_HIGH, "RESET");
 }
 
-static int exynos_pcie_establish_link(struct pcie_port *pp)
+static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
 {
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       struct pcie_port *pp = &exynos_pcie->pp;
+       struct device *dev = pp->dev;
        u32 val;
 
        if (dw_pcie_link_up(pp)) {
-               dev_err(pp->dev, "Link already up\n");
+               dev_err(dev, "Link already up\n");
                return 0;
        }
 
-       /* assert reset signals */
-       exynos_pcie_assert_core_reset(pp);
-       exynos_pcie_assert_phy_reset(pp);
-
-       /* de-assert phy reset */
-       exynos_pcie_deassert_phy_reset(pp);
-
-       /* power on phy */
-       exynos_pcie_power_on_phy(pp);
-
-       /* initialize phy */
-       exynos_pcie_init_phy(pp);
+       exynos_pcie_assert_core_reset(exynos_pcie);
+       exynos_pcie_assert_phy_reset(exynos_pcie);
+       exynos_pcie_deassert_phy_reset(exynos_pcie);
+       exynos_pcie_power_on_phy(exynos_pcie);
+       exynos_pcie_init_phy(exynos_pcie);
 
        /* pulse for common reset */
        exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_COMMON_RESET);
        udelay(500);
        exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
 
-       /* de-assert core reset */
-       exynos_pcie_deassert_core_reset(pp);
-
-       /* setup root complex */
+       exynos_pcie_deassert_core_reset(exynos_pcie);
        dw_pcie_setup_rc(pp);
-
-       /* assert reset signal */
-       exynos_pcie_assert_reset(pp);
+       exynos_pcie_assert_reset(exynos_pcie);
 
        /* assert LTSSM enable */
        exynos_elb_writel(exynos_pcie, PCIE_ELBI_LTSSM_ENABLE,
@@ -361,27 +341,23 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
 
        while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) {
                val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED);
-               dev_info(pp->dev, "PLL Locked: 0x%x\n", val);
+               dev_info(dev, "PLL Locked: 0x%x\n", val);
        }
-       /* power off phy */
-       exynos_pcie_power_off_phy(pp);
-
+       exynos_pcie_power_off_phy(exynos_pcie);
        return -ETIMEDOUT;
 }
 
-static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp)
+static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_PULSE);
        exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_PULSE);
 }
 
-static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp)
+static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        /* enable INTX interrupt */
        val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
@@ -391,23 +367,24 @@ static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp)
 
 static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
+       struct exynos_pcie *exynos_pcie = arg;
 
-       exynos_pcie_clear_irq_pulse(pp);
+       exynos_pcie_clear_irq_pulse(exynos_pcie);
        return IRQ_HANDLED;
 }
 
 static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
+       struct exynos_pcie *exynos_pcie = arg;
+       struct pcie_port *pp = &exynos_pcie->pp;
 
        return dw_handle_msi_irq(pp);
 }
 
-static void exynos_pcie_msi_init(struct pcie_port *pp)
+static void exynos_pcie_msi_init(struct exynos_pcie *exynos_pcie)
 {
+       struct pcie_port *pp = &exynos_pcie->pp;
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        dw_pcie_msi_init(pp);
 
@@ -417,60 +394,64 @@ static void exynos_pcie_msi_init(struct pcie_port *pp)
        exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_LEVEL);
 }
 
-static void exynos_pcie_enable_interrupts(struct pcie_port *pp)
+static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
 {
-       exynos_pcie_enable_irq_pulse(pp);
+       exynos_pcie_enable_irq_pulse(exynos_pcie);
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
-               exynos_pcie_msi_init(pp);
+               exynos_pcie_msi_init(exynos_pcie);
 }
 
-static inline u32 exynos_pcie_readl_rc(struct pcie_port *pp,
-                                      void __iomem *dbi_base)
+static u32 exynos_pcie_readl_rc(struct pcie_port *pp, u32 reg)
 {
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
        u32 val;
 
-       exynos_pcie_sideband_dbi_r_mode(pp, true);
-       val = readl(dbi_base);
-       exynos_pcie_sideband_dbi_r_mode(pp, false);
+       exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true);
+       val = readl(pp->dbi_base + reg);
+       exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false);
        return val;
 }
 
-static inline void exynos_pcie_writel_rc(struct pcie_port *pp,
-                                       u32 val, void __iomem *dbi_base)
+static void exynos_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val)
 {
-       exynos_pcie_sideband_dbi_w_mode(pp, true);
-       writel(val, dbi_base);
-       exynos_pcie_sideband_dbi_w_mode(pp, false);
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+       exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true);
+       writel(val, pp->dbi_base + reg);
+       exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false);
 }
 
 static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
                                u32 *val)
 {
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
        int ret;
 
-       exynos_pcie_sideband_dbi_r_mode(pp, true);
+       exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true);
        ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
-       exynos_pcie_sideband_dbi_r_mode(pp, false);
+       exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false);
        return ret;
 }
 
 static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
                                u32 val)
 {
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
        int ret;
 
-       exynos_pcie_sideband_dbi_w_mode(pp, true);
+       exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true);
        ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
-       exynos_pcie_sideband_dbi_w_mode(pp, false);
+       exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false);
        return ret;
 }
 
 static int exynos_pcie_link_up(struct pcie_port *pp)
 {
        struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
-       u32 val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_RDLH_LINKUP);
+       u32 val;
 
+       val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_RDLH_LINKUP);
        if (val == PCIE_ELBI_LTSSM_ENABLE)
                return 1;
 
@@ -479,8 +460,10 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
 
 static void exynos_pcie_host_init(struct pcie_port *pp)
 {
-       exynos_pcie_establish_link(pp);
-       exynos_pcie_enable_interrupts(pp);
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+       exynos_pcie_establish_link(exynos_pcie);
+       exynos_pcie_enable_interrupts(exynos_pcie);
 }
 
 static struct pcie_host_ops exynos_pcie_host_ops = {
@@ -492,36 +475,38 @@ static struct pcie_host_ops exynos_pcie_host_ops = {
        .host_init = exynos_pcie_host_init,
 };
 
-static int __init exynos_add_pcie_port(struct pcie_port *pp,
+static int __init exynos_add_pcie_port(struct exynos_pcie *exynos_pcie,
                                       struct platform_device *pdev)
 {
+       struct pcie_port *pp = &exynos_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        pp->irq = platform_get_irq(pdev, 1);
        if (!pp->irq) {
-               dev_err(&pdev->dev, "failed to get irq\n");
+               dev_err(dev, "failed to get irq\n");
                return -ENODEV;
        }
-       ret = devm_request_irq(&pdev->dev, pp->irq, exynos_pcie_irq_handler,
-                               IRQF_SHARED, "exynos-pcie", pp);
+       ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler,
+                               IRQF_SHARED, "exynos-pcie", exynos_pcie);
        if (ret) {
-               dev_err(&pdev->dev, "failed to request irq\n");
+               dev_err(dev, "failed to request irq\n");
                return ret;
        }
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                pp->msi_irq = platform_get_irq(pdev, 0);
                if (!pp->msi_irq) {
-                       dev_err(&pdev->dev, "failed to get msi irq\n");
+                       dev_err(dev, "failed to get msi irq\n");
                        return -ENODEV;
                }
 
-               ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+               ret = devm_request_irq(dev, pp->msi_irq,
                                        exynos_pcie_msi_irq_handler,
                                        IRQF_SHARED | IRQF_NO_THREAD,
-                                       "exynos-pcie", pp);
+                                       "exynos-pcie", exynos_pcie);
                if (ret) {
-                       dev_err(&pdev->dev, "failed to request msi irq\n");
+                       dev_err(dev, "failed to request msi irq\n");
                        return ret;
                }
        }
@@ -531,7 +516,7 @@ static int __init exynos_add_pcie_port(struct pcie_port *pp,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -540,37 +525,36 @@ static int __init exynos_add_pcie_port(struct pcie_port *pp,
 
 static int __init exynos_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct exynos_pcie *exynos_pcie;
        struct pcie_port *pp;
-       struct device_node *np = pdev->dev.of_node;
+       struct device_node *np = dev->of_node;
        struct resource *elbi_base;
        struct resource *phy_base;
        struct resource *block_base;
        int ret;
 
-       exynos_pcie = devm_kzalloc(&pdev->dev, sizeof(*exynos_pcie),
-                               GFP_KERNEL);
+       exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
        if (!exynos_pcie)
                return -ENOMEM;
 
        pp = &exynos_pcie->pp;
-
-       pp->dev = &pdev->dev;
+       pp->dev = dev;
 
        exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
 
-       exynos_pcie->clk = devm_clk_get(&pdev->dev, "pcie");
+       exynos_pcie->clk = devm_clk_get(dev, "pcie");
        if (IS_ERR(exynos_pcie->clk)) {
-               dev_err(&pdev->dev, "Failed to get pcie rc clock\n");
+               dev_err(dev, "Failed to get pcie rc clock\n");
                return PTR_ERR(exynos_pcie->clk);
        }
        ret = clk_prepare_enable(exynos_pcie->clk);
        if (ret)
                return ret;
 
-       exynos_pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
+       exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
        if (IS_ERR(exynos_pcie->bus_clk)) {
-               dev_err(&pdev->dev, "Failed to get pcie bus clock\n");
+               dev_err(dev, "Failed to get pcie bus clock\n");
                ret = PTR_ERR(exynos_pcie->bus_clk);
                goto fail_clk;
        }
@@ -579,27 +563,27 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
                goto fail_clk;
 
        elbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       exynos_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base);
+       exynos_pcie->elbi_base = devm_ioremap_resource(dev, elbi_base);
        if (IS_ERR(exynos_pcie->elbi_base)) {
                ret = PTR_ERR(exynos_pcie->elbi_base);
                goto fail_bus_clk;
        }
 
        phy_base = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       exynos_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy_base);
+       exynos_pcie->phy_base = devm_ioremap_resource(dev, phy_base);
        if (IS_ERR(exynos_pcie->phy_base)) {
                ret = PTR_ERR(exynos_pcie->phy_base);
                goto fail_bus_clk;
        }
 
        block_base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-       exynos_pcie->block_base = devm_ioremap_resource(&pdev->dev, block_base);
+       exynos_pcie->block_base = devm_ioremap_resource(dev, block_base);
        if (IS_ERR(exynos_pcie->block_base)) {
                ret = PTR_ERR(exynos_pcie->block_base);
                goto fail_bus_clk;
        }
 
-       ret = exynos_add_pcie_port(pp, pdev);
+       ret = exynos_add_pcie_port(exynos_pcie, pdev);
        if (ret < 0)
                goto fail_bus_clk;
 
index ead4a5c3480b27b12c478a56f46e2a5fed312219..c8cefb07821878a100172d28d172e5770894703c 100644 (file)
@@ -39,16 +39,15 @@ enum imx6_pcie_variants {
 };
 
 struct imx6_pcie {
+       struct pcie_port        pp;     /* pp.dbi_base is DT 0th resource */
        int                     reset_gpio;
        bool                    gpio_active_high;
        struct clk              *pcie_bus;
        struct clk              *pcie_phy;
        struct clk              *pcie_inbound_axi;
        struct clk              *pcie;
-       struct pcie_port        pp;
        struct regmap           *iomuxc_gpr;
        enum imx6_pcie_variants variant;
-       void __iomem            *mem_base;
        u32                     tx_deemph_gen1;
        u32                     tx_deemph_gen2_3p5db;
        u32                     tx_deemph_gen2_6db;
@@ -96,14 +95,15 @@ struct imx6_pcie {
 #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
 #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
 
-static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val)
+static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
        u32 val;
        u32 max_iterations = 10;
        u32 wait_counter = 0;
 
        do {
-               val = readl(dbi_base + PCIE_PHY_STAT);
+               val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT);
                val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
                wait_counter++;
 
@@ -116,123 +116,126 @@ static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val)
        return -ETIMEDOUT;
 }
 
-static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr)
+static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
        u32 val;
        int ret;
 
        val = addr << PCIE_PHY_CTRL_DATA_LOC;
-       writel(val, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
 
        val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
-       writel(val, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
 
-       ret = pcie_phy_poll_ack(dbi_base, 1);
+       ret = pcie_phy_poll_ack(imx6_pcie, 1);
        if (ret)
                return ret;
 
        val = addr << PCIE_PHY_CTRL_DATA_LOC;
-       writel(val, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
 
-       return pcie_phy_poll_ack(dbi_base, 0);
+       return pcie_phy_poll_ack(imx6_pcie, 0);
 }
 
 /* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */
-static int pcie_phy_read(void __iomem *dbi_base, int addr, int *data)
+static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
        u32 val, phy_ctl;
        int ret;
 
-       ret = pcie_phy_wait_ack(dbi_base, addr);
+       ret = pcie_phy_wait_ack(imx6_pcie, addr);
        if (ret)
                return ret;
 
        /* assert Read signal */
        phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC;
-       writel(phy_ctl, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, phy_ctl);
 
-       ret = pcie_phy_poll_ack(dbi_base, 1);
+       ret = pcie_phy_poll_ack(imx6_pcie, 1);
        if (ret)
                return ret;
 
-       val = readl(dbi_base + PCIE_PHY_STAT);
+       val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT);
        *data = val & 0xffff;
 
        /* deassert Read signal */
-       writel(0x00, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x00);
 
-       return pcie_phy_poll_ack(dbi_base, 0);
+       return pcie_phy_poll_ack(imx6_pcie, 0);
 }
 
-static int pcie_phy_write(void __iomem *dbi_base, int addr, int data)
+static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
        u32 var;
        int ret;
 
        /* write addr */
        /* cap addr */
-       ret = pcie_phy_wait_ack(dbi_base, addr);
+       ret = pcie_phy_wait_ack(imx6_pcie, addr);
        if (ret)
                return ret;
 
        var = data << PCIE_PHY_CTRL_DATA_LOC;
-       writel(var, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
 
        /* capture data */
        var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC);
-       writel(var, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
 
-       ret = pcie_phy_poll_ack(dbi_base, 1);
+       ret = pcie_phy_poll_ack(imx6_pcie, 1);
        if (ret)
                return ret;
 
        /* deassert cap data */
        var = data << PCIE_PHY_CTRL_DATA_LOC;
-       writel(var, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
 
        /* wait for ack de-assertion */
-       ret = pcie_phy_poll_ack(dbi_base, 0);
+       ret = pcie_phy_poll_ack(imx6_pcie, 0);
        if (ret)
                return ret;
 
        /* assert wr signal */
        var = 0x1 << PCIE_PHY_CTRL_WR_LOC;
-       writel(var, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
 
        /* wait for ack */
-       ret = pcie_phy_poll_ack(dbi_base, 1);
+       ret = pcie_phy_poll_ack(imx6_pcie, 1);
        if (ret)
                return ret;
 
        /* deassert wr signal */
        var = data << PCIE_PHY_CTRL_DATA_LOC;
-       writel(var, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
 
        /* wait for ack de-assertion */
-       ret = pcie_phy_poll_ack(dbi_base, 0);
+       ret = pcie_phy_poll_ack(imx6_pcie, 0);
        if (ret)
                return ret;
 
-       writel(0x0, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x0);
 
        return 0;
 }
 
-static void imx6_pcie_reset_phy(struct pcie_port *pp)
+static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)
 {
        u32 tmp;
 
-       pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp);
+       pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp);
        tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
                PHY_RX_OVRD_IN_LO_RX_PLL_EN);
-       pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
+       pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
 
        usleep_range(2000, 3000);
 
-       pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp);
+       pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp);
        tmp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
                  PHY_RX_OVRD_IN_LO_RX_PLL_EN);
-       pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
+       pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
 }
 
 /*  Added for PCI abort handling */
@@ -242,9 +245,9 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
        return 0;
 }
 
-static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
+static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
 {
-       struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+       struct pcie_port *pp = &imx6_pcie->pp;
        u32 val, gpr1, gpr12;
 
        switch (imx6_pcie->variant) {
@@ -281,10 +284,10 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
 
                if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) &&
                    (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) {
-                       val = readl(pp->dbi_base + PCIE_PL_PFLR);
+                       val = dw_pcie_readl_rc(pp, PCIE_PL_PFLR);
                        val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
                        val |= PCIE_PL_PFLR_FORCE_LINK;
-                       writel(val, pp->dbi_base + PCIE_PL_PFLR);
+                       dw_pcie_writel_rc(pp, PCIE_PL_PFLR, val);
 
                        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                           IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
@@ -296,20 +299,19 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
                                   IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
                break;
        }
-
-       return 0;
 }
 
 static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
 {
        struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
        int ret = 0;
 
        switch (imx6_pcie->variant) {
        case IMX6SX:
                ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
                if (ret) {
-                       dev_err(pp->dev, "unable to enable pcie_axi clock\n");
+                       dev_err(dev, "unable to enable pcie_axi clock\n");
                        break;
                }
 
@@ -336,32 +338,33 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
        return ret;
 }
 
-static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
+static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
 {
-       struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+       struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        ret = clk_prepare_enable(imx6_pcie->pcie_phy);
        if (ret) {
-               dev_err(pp->dev, "unable to enable pcie_phy clock\n");
-               goto err_pcie_phy;
+               dev_err(dev, "unable to enable pcie_phy clock\n");
+               return;
        }
 
        ret = clk_prepare_enable(imx6_pcie->pcie_bus);
        if (ret) {
-               dev_err(pp->dev, "unable to enable pcie_bus clock\n");
+               dev_err(dev, "unable to enable pcie_bus clock\n");
                goto err_pcie_bus;
        }
 
        ret = clk_prepare_enable(imx6_pcie->pcie);
        if (ret) {
-               dev_err(pp->dev, "unable to enable pcie clock\n");
+               dev_err(dev, "unable to enable pcie clock\n");
                goto err_pcie;
        }
 
        ret = imx6_pcie_enable_ref_clk(imx6_pcie);
        if (ret) {
-               dev_err(pp->dev, "unable to enable pcie ref clock\n");
+               dev_err(dev, "unable to enable pcie ref clock\n");
                goto err_ref_clk;
        }
 
@@ -392,7 +395,7 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
                break;
        }
 
-       return 0;
+       return;
 
 err_ref_clk:
        clk_disable_unprepare(imx6_pcie->pcie);
@@ -400,14 +403,10 @@ err_pcie:
        clk_disable_unprepare(imx6_pcie->pcie_bus);
 err_pcie_bus:
        clk_disable_unprepare(imx6_pcie->pcie_phy);
-err_pcie_phy:
-       return ret;
 }
 
-static void imx6_pcie_init_phy(struct pcie_port *pp)
+static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
 {
-       struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
-
        if (imx6_pcie->variant == IMX6SX)
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                   IMX6SX_GPR12_PCIE_RX_EQ_MASK,
@@ -439,45 +438,52 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
                           imx6_pcie->tx_swing_low << 25);
 }
 
-static int imx6_pcie_wait_for_link(struct pcie_port *pp)
+static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
+
        /* check if the link is up or not */
        if (!dw_pcie_wait_for_link(pp))
                return 0;
 
-       dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
+       dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
        return -ETIMEDOUT;
 }
 
-static int imx6_pcie_wait_for_speed_change(struct pcie_port *pp)
+static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
        u32 tmp;
        unsigned int retries;
 
        for (retries = 0; retries < 200; retries++) {
-               tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+               tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
                /* Test if the speed change finished. */
                if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
                        return 0;
                usleep_range(100, 1000);
        }
 
-       dev_err(pp->dev, "Speed change timeout\n");
+       dev_err(dev, "Speed change timeout\n");
        return -EINVAL;
 }
 
 static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
+       struct imx6_pcie *imx6_pcie = arg;
+       struct pcie_port *pp = &imx6_pcie->pp;
 
        return dw_handle_msi_irq(pp);
 }
 
-static int imx6_pcie_establish_link(struct pcie_port *pp)
+static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
 {
-       struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+       struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
        u32 tmp;
        int ret;
 
@@ -486,76 +492,73 @@ static int imx6_pcie_establish_link(struct pcie_port *pp)
         * started in Gen2 mode, there is a possibility the devices on the
         * bus will not be detected at all.  This happens with PCIe switches.
         */
-       tmp = readl(pp->dbi_base + PCIE_RC_LCR);
+       tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR);
        tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
        tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
-       writel(tmp, pp->dbi_base + PCIE_RC_LCR);
+       dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp);
 
        /* Start LTSSM. */
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                        IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
 
-       ret = imx6_pcie_wait_for_link(pp);
+       ret = imx6_pcie_wait_for_link(imx6_pcie);
        if (ret) {
-               dev_info(pp->dev, "Link never came up\n");
+               dev_info(dev, "Link never came up\n");
                goto err_reset_phy;
        }
 
        if (imx6_pcie->link_gen == 2) {
                /* Allow Gen2 mode after the link is up. */
-               tmp = readl(pp->dbi_base + PCIE_RC_LCR);
+               tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR);
                tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
                tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
-               writel(tmp, pp->dbi_base + PCIE_RC_LCR);
+               dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp);
        } else {
-               dev_info(pp->dev, "Link: Gen2 disabled\n");
+               dev_info(dev, "Link: Gen2 disabled\n");
        }
 
        /*
         * Start Directed Speed Change so the best possible speed both link
         * partners support can be negotiated.
         */
-       tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+       tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
        tmp |= PORT_LOGIC_SPEED_CHANGE;
-       writel(tmp, pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+       dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
 
-       ret = imx6_pcie_wait_for_speed_change(pp);
+       ret = imx6_pcie_wait_for_speed_change(imx6_pcie);
        if (ret) {
-               dev_err(pp->dev, "Failed to bring link up!\n");
+               dev_err(dev, "Failed to bring link up!\n");
                goto err_reset_phy;
        }
 
        /* Make sure link training is finished as well! */
-       ret = imx6_pcie_wait_for_link(pp);
+       ret = imx6_pcie_wait_for_link(imx6_pcie);
        if (ret) {
-               dev_err(pp->dev, "Failed to bring link up!\n");
+               dev_err(dev, "Failed to bring link up!\n");
                goto err_reset_phy;
        }
 
-       tmp = readl(pp->dbi_base + PCIE_RC_LCSR);
-       dev_info(pp->dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
+       tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCSR);
+       dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
        return 0;
 
 err_reset_phy:
-       dev_dbg(pp->dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
-       imx6_pcie_reset_phy(pp);
-
+       dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
+       imx6_pcie_reset_phy(imx6_pcie);
        return ret;
 }
 
 static void imx6_pcie_host_init(struct pcie_port *pp)
 {
-       imx6_pcie_assert_core_reset(pp);
-
-       imx6_pcie_init_phy(pp);
-
-       imx6_pcie_deassert_core_reset(pp);
+       struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
 
+       imx6_pcie_assert_core_reset(imx6_pcie);
+       imx6_pcie_init_phy(imx6_pcie);
+       imx6_pcie_deassert_core_reset(imx6_pcie);
        dw_pcie_setup_rc(pp);
-
-       imx6_pcie_establish_link(pp);
+       imx6_pcie_establish_link(imx6_pcie);
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
                dw_pcie_msi_init(pp);
@@ -563,7 +566,7 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
 
 static int imx6_pcie_link_up(struct pcie_port *pp)
 {
-       return readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) &
+       return dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1) &
                        PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
 }
 
@@ -572,24 +575,26 @@ static struct pcie_host_ops imx6_pcie_host_ops = {
        .host_init = imx6_pcie_host_init,
 };
 
-static int __init imx6_add_pcie_port(struct pcie_port *pp,
-                       struct platform_device *pdev)
+static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
+                                    struct platform_device *pdev)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                pp->msi_irq = platform_get_irq_byname(pdev, "msi");
                if (pp->msi_irq <= 0) {
-                       dev_err(&pdev->dev, "failed to get MSI irq\n");
+                       dev_err(dev, "failed to get MSI irq\n");
                        return -ENODEV;
                }
 
-               ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+               ret = devm_request_irq(dev, pp->msi_irq,
                                       imx6_pcie_msi_handler,
                                       IRQF_SHARED | IRQF_NO_THREAD,
-                                      "mx6-pcie-msi", pp);
+                                      "mx6-pcie-msi", imx6_pcie);
                if (ret) {
-                       dev_err(&pdev->dev, "failed to request MSI irq\n");
+                       dev_err(dev, "failed to request MSI irq\n");
                        return ret;
                }
        }
@@ -599,7 +604,7 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -608,75 +613,72 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp,
 
 static int __init imx6_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct imx6_pcie *imx6_pcie;
        struct pcie_port *pp;
-       struct device_node *np = pdev->dev.of_node;
        struct resource *dbi_base;
-       struct device_node *node = pdev->dev.of_node;
+       struct device_node *node = dev->of_node;
        int ret;
 
-       imx6_pcie = devm_kzalloc(&pdev->dev, sizeof(*imx6_pcie), GFP_KERNEL);
+       imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL);
        if (!imx6_pcie)
                return -ENOMEM;
 
        pp = &imx6_pcie->pp;
-       pp->dev = &pdev->dev;
+       pp->dev = dev;
 
        imx6_pcie->variant =
-               (enum imx6_pcie_variants)of_device_get_match_data(&pdev->dev);
+               (enum imx6_pcie_variants)of_device_get_match_data(dev);
 
        /* Added for PCI abort handling */
        hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
                "imprecise external abort");
 
        dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base);
+       pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
        if (IS_ERR(pp->dbi_base))
                return PTR_ERR(pp->dbi_base);
 
        /* Fetch GPIOs */
-       imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
-       imx6_pcie->gpio_active_high = of_property_read_bool(np,
+       imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0);
+       imx6_pcie->gpio_active_high = of_property_read_bool(node,
                                                "reset-gpio-active-high");
        if (gpio_is_valid(imx6_pcie->reset_gpio)) {
-               ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio,
+               ret = devm_gpio_request_one(dev, imx6_pcie->reset_gpio,
                                imx6_pcie->gpio_active_high ?
                                        GPIOF_OUT_INIT_HIGH :
                                        GPIOF_OUT_INIT_LOW,
                                "PCIe reset");
                if (ret) {
-                       dev_err(&pdev->dev, "unable to get reset gpio\n");
+                       dev_err(dev, "unable to get reset gpio\n");
                        return ret;
                }
        }
 
        /* Fetch clocks */
-       imx6_pcie->pcie_phy = devm_clk_get(&pdev->dev, "pcie_phy");
+       imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy");
        if (IS_ERR(imx6_pcie->pcie_phy)) {
-               dev_err(&pdev->dev,
-                       "pcie_phy clock source missing or invalid\n");
+               dev_err(dev, "pcie_phy clock source missing or invalid\n");
                return PTR_ERR(imx6_pcie->pcie_phy);
        }
 
-       imx6_pcie->pcie_bus = devm_clk_get(&pdev->dev, "pcie_bus");
+       imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus");
        if (IS_ERR(imx6_pcie->pcie_bus)) {
-               dev_err(&pdev->dev,
-                       "pcie_bus clock source missing or invalid\n");
+               dev_err(dev, "pcie_bus clock source missing or invalid\n");
                return PTR_ERR(imx6_pcie->pcie_bus);
        }
 
-       imx6_pcie->pcie = devm_clk_get(&pdev->dev, "pcie");
+       imx6_pcie->pcie = devm_clk_get(dev, "pcie");
        if (IS_ERR(imx6_pcie->pcie)) {
-               dev_err(&pdev->dev,
-                       "pcie clock source missing or invalid\n");
+               dev_err(dev, "pcie clock source missing or invalid\n");
                return PTR_ERR(imx6_pcie->pcie);
        }
 
        if (imx6_pcie->variant == IMX6SX) {
-               imx6_pcie->pcie_inbound_axi = devm_clk_get(&pdev->dev,
+               imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
                                                           "pcie_inbound_axi");
                if (IS_ERR(imx6_pcie->pcie_inbound_axi)) {
-                       dev_err(&pdev->dev,
+                       dev_err(dev,
                                "pcie_incbound_axi clock missing or invalid\n");
                        return PTR_ERR(imx6_pcie->pcie_inbound_axi);
                }
@@ -686,7 +688,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
        imx6_pcie->iomuxc_gpr =
                 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
        if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
-               dev_err(&pdev->dev, "unable to find iomuxc registers\n");
+               dev_err(dev, "unable to find iomuxc registers\n");
                return PTR_ERR(imx6_pcie->iomuxc_gpr);
        }
 
@@ -712,12 +714,12 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                imx6_pcie->tx_swing_low = 127;
 
        /* Limit link speed */
-       ret = of_property_read_u32(pp->dev->of_node, "fsl,max-link-speed",
+       ret = of_property_read_u32(node, "fsl,max-link-speed",
                                   &imx6_pcie->link_gen);
        if (ret)
                imx6_pcie->link_gen = 1;
 
-       ret = imx6_add_pcie_port(pp, pdev);
+       ret = imx6_add_pcie_port(imx6_pcie, pdev);
        if (ret < 0)
                return ret;
 
@@ -730,7 +732,7 @@ static void imx6_pcie_shutdown(struct platform_device *pdev)
        struct imx6_pcie *imx6_pcie = platform_get_drvdata(pdev);
 
        /* bring down link, so bootloader gets clean state in case of reboot */
-       imx6_pcie_assert_core_reset(&imx6_pcie->pp);
+       imx6_pcie_assert_core_reset(imx6_pcie);
 }
 
 static const struct of_device_id imx6_pcie_of_match[] = {
index 41515092eb0de7992769b3ea5ddec8117c44d2ec..9397c46671062b011e6a6f9f523f9d2d0b95c1c4 100644 (file)
@@ -88,13 +88,24 @@ phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
        return ks_pcie->app.start + MSI_IRQ;
 }
 
+static u32 ks_dw_app_readl(struct keystone_pcie *ks_pcie, u32 offset)
+{
+       return readl(ks_pcie->va_app_base + offset);
+}
+
+static void ks_dw_app_writel(struct keystone_pcie *ks_pcie, u32 offset, u32 val)
+{
+       writel(val, ks_pcie->va_app_base + offset);
+}
+
 void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset)
 {
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        u32 pending, vector;
        int src, virq;
 
-       pending = readl(ks_pcie->va_app_base + MSI0_IRQ_STATUS + (offset << 4));
+       pending = ks_dw_app_readl(ks_pcie, MSI0_IRQ_STATUS + (offset << 4));
 
        /*
         * MSI0 status bit 0-3 shows vectors 0, 8, 16, 24, MSI1 status bit
@@ -104,7 +115,7 @@ void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset)
                if (BIT(src) & pending) {
                        vector = offset + (src << 3);
                        virq = irq_linear_revmap(pp->irq_domain, vector);
-                       dev_dbg(pp->dev, "irq: bit %d, vector %d, virq %d\n",
+                       dev_dbg(dev, "irq: bit %d, vector %d, virq %d\n",
                                src, vector, virq);
                        generic_handle_irq(virq);
                }
@@ -124,9 +135,9 @@ static void ks_dw_pcie_msi_irq_ack(struct irq_data *d)
        offset = d->irq - irq_linear_revmap(pp->irq_domain, 0);
        update_reg_offset_bit_pos(offset, &reg_offset, &bit_pos);
 
-       writel(BIT(bit_pos),
-              ks_pcie->va_app_base + MSI0_IRQ_STATUS + (reg_offset << 4));
-       writel(reg_offset + MSI_IRQ_OFFSET, ks_pcie->va_app_base + IRQ_EOI);
+       ks_dw_app_writel(ks_pcie, MSI0_IRQ_STATUS + (reg_offset << 4),
+                        BIT(bit_pos));
+       ks_dw_app_writel(ks_pcie, IRQ_EOI, reg_offset + MSI_IRQ_OFFSET);
 }
 
 void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
@@ -135,8 +146,8 @@ void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
        struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
 
        update_reg_offset_bit_pos(irq, &reg_offset, &bit_pos);
-       writel(BIT(bit_pos),
-              ks_pcie->va_app_base + MSI0_IRQ_ENABLE_SET + (reg_offset << 4));
+       ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_SET + (reg_offset << 4),
+                        BIT(bit_pos));
 }
 
 void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
@@ -145,8 +156,8 @@ void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
        struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
 
        update_reg_offset_bit_pos(irq, &reg_offset, &bit_pos);
-       writel(BIT(bit_pos),
-              ks_pcie->va_app_base + MSI0_IRQ_ENABLE_CLR + (reg_offset << 4));
+       ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_CLR + (reg_offset << 4),
+                        BIT(bit_pos));
 }
 
 static void ks_dw_pcie_msi_irq_mask(struct irq_data *d)
@@ -215,6 +226,7 @@ static const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = {
 int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip)
 {
        struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+       struct device *dev = pp->dev;
        int i;
 
        pp->irq_domain = irq_domain_add_linear(ks_pcie->msi_intc_np,
@@ -222,7 +234,7 @@ int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip)
                                        &ks_dw_pcie_msi_domain_ops,
                                        chip);
        if (!pp->irq_domain) {
-               dev_err(pp->dev, "irq domain init failed\n");
+               dev_err(dev, "irq domain init failed\n");
                return -ENXIO;
        }
 
@@ -237,47 +249,47 @@ void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie)
        int i;
 
        for (i = 0; i < MAX_LEGACY_IRQS; i++)
-               writel(0x1, ks_pcie->va_app_base + IRQ_ENABLE_SET + (i << 4));
+               ks_dw_app_writel(ks_pcie, IRQ_ENABLE_SET + (i << 4), 0x1);
 }
 
 void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset)
 {
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        u32 pending;
        int virq;
 
-       pending = readl(ks_pcie->va_app_base + IRQ_STATUS + (offset << 4));
+       pending = ks_dw_app_readl(ks_pcie, IRQ_STATUS + (offset << 4));
 
        if (BIT(0) & pending) {
                virq = irq_linear_revmap(ks_pcie->legacy_irq_domain, offset);
-               dev_dbg(pp->dev, ": irq: irq_offset %d, virq %d\n", offset,
-                       virq);
+               dev_dbg(dev, ": irq: irq_offset %d, virq %d\n", offset, virq);
                generic_handle_irq(virq);
        }
 
        /* EOI the INTx interrupt */
-       writel(offset, ks_pcie->va_app_base + IRQ_EOI);
+       ks_dw_app_writel(ks_pcie, IRQ_EOI, offset);
 }
 
-void ks_dw_pcie_enable_error_irq(void __iomem *reg_base)
+void ks_dw_pcie_enable_error_irq(struct keystone_pcie *ks_pcie)
 {
-       writel(ERR_IRQ_ALL, reg_base + ERR_IRQ_ENABLE_SET);
+       ks_dw_app_writel(ks_pcie, ERR_IRQ_ENABLE_SET, ERR_IRQ_ALL);
 }
 
-irqreturn_t ks_dw_pcie_handle_error_irq(struct device *dev,
-                                       void __iomem *reg_base)
+irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie)
 {
        u32 status;
 
-       status = readl(reg_base + ERR_IRQ_STATUS_RAW) & ERR_IRQ_ALL;
+       status = ks_dw_app_readl(ks_pcie, ERR_IRQ_STATUS_RAW) & ERR_IRQ_ALL;
        if (!status)
                return IRQ_NONE;
 
        if (status & ERR_FATAL_IRQ)
-               dev_err(dev, "fatal error (status %#010x)\n", status);
+               dev_err(ks_pcie->pp.dev, "fatal error (status %#010x)\n",
+                       status);
 
        /* Ack the IRQ; status bits are RW1C */
-       writel(status, reg_base + ERR_IRQ_STATUS);
+       ks_dw_app_writel(ks_pcie, ERR_IRQ_STATUS, status);
        return IRQ_HANDLED;
 }
 
@@ -322,15 +334,15 @@ static const struct irq_domain_ops ks_dw_pcie_legacy_irq_domain_ops = {
  * Since modification of dbi_cs2 involves different clock domain, read the
  * status back to ensure the transition is complete.
  */
-static void ks_dw_pcie_set_dbi_mode(void __iomem *reg_virt)
+static void ks_dw_pcie_set_dbi_mode(struct keystone_pcie *ks_pcie)
 {
        u32 val;
 
-       writel(DBI_CS2_EN_VAL | readl(reg_virt + CMD_STATUS),
-              reg_virt + CMD_STATUS);
+       val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
+       ks_dw_app_writel(ks_pcie, CMD_STATUS, DBI_CS2_EN_VAL | val);
 
        do {
-               val = readl(reg_virt + CMD_STATUS);
+               val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
        } while (!(val & DBI_CS2_EN_VAL));
 }
 
@@ -340,15 +352,15 @@ static void ks_dw_pcie_set_dbi_mode(void __iomem *reg_virt)
  * Since modification of dbi_cs2 involves different clock domain, read the
  * status back to ensure the transition is complete.
  */
-static void ks_dw_pcie_clear_dbi_mode(void __iomem *reg_virt)
+static void ks_dw_pcie_clear_dbi_mode(struct keystone_pcie *ks_pcie)
 {
        u32 val;
 
-       writel(~DBI_CS2_EN_VAL & readl(reg_virt + CMD_STATUS),
-                    reg_virt + CMD_STATUS);
+       val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
+       ks_dw_app_writel(ks_pcie, CMD_STATUS, ~DBI_CS2_EN_VAL & val);
 
        do {
-               val = readl(reg_virt + CMD_STATUS);
+               val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
        } while (val & DBI_CS2_EN_VAL);
 }
 
@@ -357,28 +369,29 @@ void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
        struct pcie_port *pp = &ks_pcie->pp;
        u32 start = pp->mem->start, end = pp->mem->end;
        int i, tr_size;
+       u32 val;
 
        /* Disable BARs for inbound access */
-       ks_dw_pcie_set_dbi_mode(ks_pcie->va_app_base);
-       writel(0, pp->dbi_base + PCI_BASE_ADDRESS_0);
-       writel(0, pp->dbi_base + PCI_BASE_ADDRESS_1);
-       ks_dw_pcie_clear_dbi_mode(ks_pcie->va_app_base);
+       ks_dw_pcie_set_dbi_mode(ks_pcie);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 0);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_1, 0);
+       ks_dw_pcie_clear_dbi_mode(ks_pcie);
 
        /* Set outbound translation size per window division */
-       writel(CFG_PCIM_WIN_SZ_IDX & 0x7, ks_pcie->va_app_base + OB_SIZE);
+       ks_dw_app_writel(ks_pcie, OB_SIZE, CFG_PCIM_WIN_SZ_IDX & 0x7);
 
        tr_size = (1 << (CFG_PCIM_WIN_SZ_IDX & 0x7)) * SZ_1M;
 
        /* Using Direct 1:1 mapping of RC <-> PCI memory space */
        for (i = 0; (i < CFG_PCIM_WIN_CNT) && (start < end); i++) {
-               writel(start | 1, ks_pcie->va_app_base + OB_OFFSET_INDEX(i));
-               writel(0, ks_pcie->va_app_base + OB_OFFSET_HI(i));
+               ks_dw_app_writel(ks_pcie, OB_OFFSET_INDEX(i), start | 1);
+               ks_dw_app_writel(ks_pcie, OB_OFFSET_HI(i), 0);
                start += tr_size;
        }
 
        /* Enable OB translation */
-       writel(OB_XLAT_EN_VAL | readl(ks_pcie->va_app_base + CMD_STATUS),
-              ks_pcie->va_app_base + CMD_STATUS);
+       val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
+       ks_dw_app_writel(ks_pcie, CMD_STATUS, OB_XLAT_EN_VAL | val);
 }
 
 /**
@@ -418,7 +431,7 @@ static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus,
        if (bus != 1)
                regval |= BIT(24);
 
-       writel(regval, ks_pcie->va_app_base + CFG_SETUP);
+       ks_dw_app_writel(ks_pcie, CFG_SETUP, regval);
        return pp->va_cfg0_base;
 }
 
@@ -456,19 +469,19 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
        struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
 
        /* Configure and set up BAR0 */
-       ks_dw_pcie_set_dbi_mode(ks_pcie->va_app_base);
+       ks_dw_pcie_set_dbi_mode(ks_pcie);
 
        /* Enable BAR0 */
-       writel(1, pp->dbi_base + PCI_BASE_ADDRESS_0);
-       writel(SZ_4K - 1, pp->dbi_base + PCI_BASE_ADDRESS_0);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 1);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, SZ_4K - 1);
 
-       ks_dw_pcie_clear_dbi_mode(ks_pcie->va_app_base);
+       ks_dw_pcie_clear_dbi_mode(ks_pcie);
 
         /*
          * For BAR0, just setting bus address for inbound writes (MSI) should
          * be sufficient.  Use physical address to avoid any conflicts.
          */
-       writel(ks_pcie->app.start, pp->dbi_base + PCI_BASE_ADDRESS_0);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, ks_pcie->app.start);
 }
 
 /**
@@ -476,8 +489,9 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
  */
 int ks_dw_pcie_link_up(struct pcie_port *pp)
 {
-       u32 val = readl(pp->dbi_base + DEBUG0);
+       u32 val;
 
+       val = dw_pcie_readl_rc(pp, DEBUG0);
        return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0;
 }
 
@@ -486,13 +500,13 @@ void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie)
        u32 val;
 
        /* Disable Link training */
-       val = readl(ks_pcie->va_app_base + CMD_STATUS);
+       val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
        val &= ~LTSSM_EN_VAL;
-       writel(LTSSM_EN_VAL | val,  ks_pcie->va_app_base + CMD_STATUS);
+       ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val);
 
        /* Initiate Link Training */
-       val = readl(ks_pcie->va_app_base + CMD_STATUS);
-       writel(LTSSM_EN_VAL | val,  ks_pcie->va_app_base + CMD_STATUS);
+       val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
+       ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val);
 }
 
 /**
@@ -506,12 +520,13 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
                                struct device_node *msi_intc_np)
 {
        struct pcie_port *pp = &ks_pcie->pp;
-       struct platform_device *pdev = to_platform_device(pp->dev);
+       struct device *dev = pp->dev;
+       struct platform_device *pdev = to_platform_device(dev);
        struct resource *res;
 
        /* Index 0 is the config reg. space address */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       pp->dbi_base = devm_ioremap_resource(pp->dev, res);
+       pp->dbi_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(pp->dbi_base))
                return PTR_ERR(pp->dbi_base);
 
@@ -524,7 +539,7 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
 
        /* Index 1 is the application reg. space address */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       ks_pcie->va_app_base = devm_ioremap_resource(pp->dev, res);
+       ks_pcie->va_app_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(ks_pcie->va_app_base))
                return PTR_ERR(ks_pcie->va_app_base);
 
@@ -537,7 +552,7 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
                                        &ks_dw_pcie_legacy_irq_domain_ops,
                                        NULL);
        if (!ks_pcie->legacy_irq_domain) {
-               dev_err(pp->dev, "Failed to add irq domain for legacy irqs\n");
+               dev_err(dev, "Failed to add irq domain for legacy irqs\n");
                return -EINVAL;
        }
 
index 82b461b5b08a14e2147700d99885e282918c303e..043c19a05da1239e54b59b14e9e4c427f6d92ccb 100644 (file)
@@ -89,12 +89,13 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_mrrs);
 static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
 {
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        unsigned int retries;
 
        dw_pcie_setup_rc(pp);
 
        if (dw_pcie_link_up(pp)) {
-               dev_err(pp->dev, "Link already up\n");
+               dev_err(dev, "Link already up\n");
                return 0;
        }
 
@@ -105,7 +106,7 @@ static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
                        return 0;
        }
 
-       dev_err(pp->dev, "phy link never came up\n");
+       dev_err(dev, "phy link never came up\n");
        return -ETIMEDOUT;
 }
 
@@ -115,9 +116,10 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc)
        struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
        u32 offset = irq - ks_pcie->msi_host_irqs[0];
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        struct irq_chip *chip = irq_desc_get_chip(desc);
 
-       dev_dbg(pp->dev, "%s, irq %d\n", __func__, irq);
+       dev_dbg(dev, "%s, irq %d\n", __func__, irq);
 
        /*
         * The chained irq handler installation would have replaced normal
@@ -142,10 +144,11 @@ static void ks_pcie_legacy_irq_handler(struct irq_desc *desc)
        unsigned int irq = irq_desc_get_irq(desc);
        struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0];
        struct irq_chip *chip = irq_desc_get_chip(desc);
 
-       dev_dbg(pp->dev, ": Handling legacy irq %d\n", irq);
+       dev_dbg(dev, ": Handling legacy irq %d\n", irq);
 
        /*
         * The chained irq handler installation would have replaced normal
@@ -234,7 +237,7 @@ static void ks_pcie_setup_interrupts(struct keystone_pcie *ks_pcie)
        }
 
        if (ks_pcie->error_irq > 0)
-               ks_dw_pcie_enable_error_irq(ks_pcie->va_app_base);
+               ks_dw_pcie_enable_error_irq(ks_pcie);
 }
 
 /*
@@ -302,14 +305,14 @@ static irqreturn_t pcie_err_irq_handler(int irq, void *priv)
 {
        struct keystone_pcie *ks_pcie = priv;
 
-       return ks_dw_pcie_handle_error_irq(ks_pcie->pp.dev,
-                                          ks_pcie->va_app_base);
+       return ks_dw_pcie_handle_error_irq(ks_pcie);
 }
 
 static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
                         struct platform_device *pdev)
 {
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        ret = ks_pcie_get_irq_controller_info(ks_pcie,
@@ -332,12 +335,12 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
         */
        ks_pcie->error_irq = irq_of_parse_and_map(ks_pcie->np, 0);
        if (ks_pcie->error_irq <= 0)
-               dev_info(&pdev->dev, "no error IRQ defined\n");
+               dev_info(dev, "no error IRQ defined\n");
        else {
                ret = request_irq(ks_pcie->error_irq, pcie_err_irq_handler,
                                  IRQF_SHARED, "pcie-error-irq", ks_pcie);
                if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to request error IRQ %d\n",
+                       dev_err(dev, "failed to request error IRQ %d\n",
                                ks_pcie->error_irq);
                        return ret;
                }
@@ -347,7 +350,7 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
        pp->ops = &keystone_pcie_host_ops;
        ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -381,12 +384,12 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
        struct phy *phy;
        int ret;
 
-       ks_pcie = devm_kzalloc(&pdev->dev, sizeof(*ks_pcie),
-                               GFP_KERNEL);
+       ks_pcie = devm_kzalloc(dev, sizeof(*ks_pcie), GFP_KERNEL);
        if (!ks_pcie)
                return -ENOMEM;
 
        pp = &ks_pcie->pp;
+       pp->dev = dev;
 
        /* initialize SerDes Phy if present */
        phy = devm_phy_get(dev, "pcie-phy");
@@ -408,7 +411,6 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
        devm_iounmap(dev, reg_p);
        devm_release_mem_region(dev, res->start, resource_size(res));
 
-       pp->dev = dev;
        ks_pcie->np = dev->of_node;
        platform_set_drvdata(pdev, ks_pcie);
        ks_pcie->clk = devm_clk_get(dev, "pcie");
index a5b0cb2ba4d78e1455668e087afb4be6fe3f6009..bc54bafda068a791f3a19c78b17fa156754c2a6e 100644 (file)
@@ -17,8 +17,8 @@
 #define MAX_LEGACY_HOST_IRQS           4
 
 struct keystone_pcie {
+       struct  pcie_port       pp;             /* pp.dbi_base is DT 0th res */
        struct  clk             *clk;
-       struct  pcie_port       pp;
        /* PCI Device ID */
        u32                     device_id;
        int                     num_legacy_host_irqs;
@@ -34,7 +34,7 @@ struct keystone_pcie {
        int error_irq;
 
        /* Application register space */
-       void __iomem            *va_app_base;
+       void __iomem            *va_app_base;   /* DT 1st resource */
        struct resource         app;
 };
 
@@ -45,9 +45,8 @@ phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
 /* Keystone specific PCI controller APIs */
 void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie);
 void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset);
-void ks_dw_pcie_enable_error_irq(void __iomem *reg_base);
-irqreturn_t ks_dw_pcie_handle_error_irq(struct device *dev,
-                                       void __iomem *reg_base);
+void ks_dw_pcie_enable_error_irq(struct keystone_pcie *ks_pcie);
+irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie);
 int  ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
                        struct device_node *msi_intc_np);
 int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
index 114ba819277af73772696f05cd3c8669150b4aca..2cb7315e26d089701679e23a7fc3f8a9ce74e4cf 100644 (file)
@@ -45,10 +45,9 @@ struct ls_pcie_drvdata {
 };
 
 struct ls_pcie {
-       void __iomem *dbi;
+       struct pcie_port pp;            /* pp.dbi_base is DT regs */
        void __iomem *lut;
        struct regmap *scfg;
-       struct pcie_port pp;
        const struct ls_pcie_drvdata *drvdata;
        int index;
 };
@@ -59,7 +58,7 @@ static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
 {
        u32 header_type;
 
-       header_type = ioread8(pcie->dbi + PCI_HEADER_TYPE);
+       header_type = ioread8(pcie->pp.dbi_base + PCI_HEADER_TYPE);
        header_type &= 0x7f;
 
        return header_type == PCI_HEADER_TYPE_BRIDGE;
@@ -68,13 +67,13 @@ static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
 /* Clear multi-function bit */
 static void ls_pcie_clear_multifunction(struct ls_pcie *pcie)
 {
-       iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->dbi + PCI_HEADER_TYPE);
+       iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->pp.dbi_base + PCI_HEADER_TYPE);
 }
 
 /* Fix class value */
 static void ls_pcie_fix_class(struct ls_pcie *pcie)
 {
-       iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->dbi + PCI_CLASS_DEVICE);
+       iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->pp.dbi_base + PCI_CLASS_DEVICE);
 }
 
 /* Drop MSG TLP except for Vendor MSG */
@@ -82,9 +81,9 @@ static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie)
 {
        u32 val;
 
-       val = ioread32(pcie->dbi + PCIE_STRFMR1);
+       val = ioread32(pcie->pp.dbi_base + PCIE_STRFMR1);
        val &= 0xDFFFFFFF;
-       iowrite32(val, pcie->dbi + PCIE_STRFMR1);
+       iowrite32(val, pcie->pp.dbi_base + PCIE_STRFMR1);
 }
 
 static int ls1021_pcie_link_up(struct pcie_port *pp)
@@ -106,18 +105,19 @@ static int ls1021_pcie_link_up(struct pcie_port *pp)
 
 static void ls1021_pcie_host_init(struct pcie_port *pp)
 {
+       struct device *dev = pp->dev;
        struct ls_pcie *pcie = to_ls_pcie(pp);
        u32 index[2];
 
-       pcie->scfg = syscon_regmap_lookup_by_phandle(pp->dev->of_node,
+       pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node,
                                                     "fsl,pcie-scfg");
        if (IS_ERR(pcie->scfg)) {
-               dev_err(pp->dev, "No syscfg phandle specified\n");
+               dev_err(dev, "No syscfg phandle specified\n");
                pcie->scfg = NULL;
                return;
        }
 
-       if (of_property_read_u32_array(pp->dev->of_node,
+       if (of_property_read_u32_array(dev->of_node,
                                       "fsl,pcie-scfg", index, 2)) {
                pcie->scfg = NULL;
                return;
@@ -148,18 +148,19 @@ static void ls_pcie_host_init(struct pcie_port *pp)
 {
        struct ls_pcie *pcie = to_ls_pcie(pp);
 
-       iowrite32(1, pcie->dbi + PCIE_DBI_RO_WR_EN);
+       iowrite32(1, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN);
        ls_pcie_fix_class(pcie);
        ls_pcie_clear_multifunction(pcie);
        ls_pcie_drop_msg_tlp(pcie);
-       iowrite32(0, pcie->dbi + PCIE_DBI_RO_WR_EN);
+       iowrite32(0, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN);
 }
 
 static int ls_pcie_msi_host_init(struct pcie_port *pp,
                                 struct msi_controller *chip)
 {
+       struct device *dev = pp->dev;
+       struct device_node *np = dev->of_node;
        struct device_node *msi_node;
-       struct device_node *np = pp->dev->of_node;
 
        /*
         * The MSI domain is set by the generic of_msi_configure().  This
@@ -169,7 +170,7 @@ static int ls_pcie_msi_host_init(struct pcie_port *pp,
         */
        msi_node = of_parse_phandle(np, "msi-parent", 0);
        if (!msi_node) {
-               dev_err(pp->dev, "failed to find msi-parent\n");
+               dev_err(dev, "failed to find msi-parent\n");
                return -EINVAL;
        }
 
@@ -212,19 +213,15 @@ static const struct of_device_id ls_pcie_of_match[] = {
        { },
 };
 
-static int __init ls_add_pcie_port(struct pcie_port *pp,
-                                  struct platform_device *pdev)
+static int __init ls_add_pcie_port(struct ls_pcie *pcie)
 {
+       struct pcie_port *pp = &pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
-       struct ls_pcie *pcie = to_ls_pcie(pp);
-
-       pp->dev = &pdev->dev;
-       pp->dbi_base = pcie->dbi;
-       pp->ops = pcie->drvdata->ops;
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(pp->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -233,38 +230,42 @@ static int __init ls_add_pcie_port(struct pcie_port *pp,
 
 static int __init ls_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        const struct of_device_id *match;
        struct ls_pcie *pcie;
+       struct pcie_port *pp;
        struct resource *dbi_base;
        int ret;
 
-       match = of_match_device(ls_pcie_of_match, &pdev->dev);
+       match = of_match_device(ls_pcie_of_match, dev);
        if (!match)
                return -ENODEV;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
+       pp = &pcie->pp;
+       pp->dev = dev;
+       pp->ops = pcie->drvdata->ops;
+
        dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
-       pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base);
-       if (IS_ERR(pcie->dbi)) {
-               dev_err(&pdev->dev, "missing *regs* space\n");
-               return PTR_ERR(pcie->dbi);
+       pcie->pp.dbi_base = devm_ioremap_resource(dev, dbi_base);
+       if (IS_ERR(pcie->pp.dbi_base)) {
+               dev_err(dev, "missing *regs* space\n");
+               return PTR_ERR(pcie->pp.dbi_base);
        }
 
        pcie->drvdata = match->data;
-       pcie->lut = pcie->dbi + pcie->drvdata->lut_offset;
+       pcie->lut = pcie->pp.dbi_base + pcie->drvdata->lut_offset;
 
        if (!ls_pcie_is_bridge(pcie))
                return -ENODEV;
 
-       ret = ls_add_pcie_port(&pcie->pp, pdev);
+       ret = ls_add_pcie_port(pcie);
        if (ret < 0)
                return ret;
 
-       platform_set_drvdata(pdev, pcie);
-
        return 0;
 }
 
index 307f81d6b479af4e55ed4e1ab30ba1cfdfdf22d2..45a89d969700bc4dd703be1ce02baa5731e1ceea 100644 (file)
@@ -1190,13 +1190,13 @@ static void mvebu_pcie_powerdown(struct mvebu_pcie_port *port)
 
 static int mvebu_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct mvebu_pcie *pcie;
-       struct device_node *np = pdev->dev.of_node;
+       struct device_node *np = dev->of_node;
        struct device_node *child;
        int num, i, ret;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pcie),
-                           GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
@@ -1206,7 +1206,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
        /* Get the PCIe memory and I/O aperture */
        mvebu_mbus_get_pcie_mem_aperture(&pcie->mem);
        if (resource_size(&pcie->mem) == 0) {
-               dev_err(&pdev->dev, "invalid memory aperture size\n");
+               dev_err(dev, "invalid memory aperture size\n");
                return -EINVAL;
        }
 
@@ -1224,20 +1224,18 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
        /* Get the bus range */
        ret = of_pci_parse_bus_range(np, &pcie->busn);
        if (ret) {
-               dev_err(&pdev->dev, "failed to parse bus-range property: %d\n",
-                       ret);
+               dev_err(dev, "failed to parse bus-range property: %d\n", ret);
                return ret;
        }
 
-       num = of_get_available_child_count(pdev->dev.of_node);
+       num = of_get_available_child_count(np);
 
-       pcie->ports = devm_kcalloc(&pdev->dev, num, sizeof(*pcie->ports),
-                                  GFP_KERNEL);
+       pcie->ports = devm_kcalloc(dev, num, sizeof(*pcie->ports), GFP_KERNEL);
        if (!pcie->ports)
                return -ENOMEM;
 
        i = 0;
-       for_each_available_child_of_node(pdev->dev.of_node, child) {
+       for_each_available_child_of_node(np, child) {
                struct mvebu_pcie_port *port = &pcie->ports[i];
 
                ret = mvebu_pcie_parse_port(pcie, port, child);
@@ -1266,8 +1264,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
 
                port->base = mvebu_pcie_map_registers(pdev, child, port);
                if (IS_ERR(port->base)) {
-                       dev_err(&pdev->dev, "%s: cannot map registers\n",
-                               port->name);
+                       dev_err(dev, "%s: cannot map registers\n", port->name);
                        port->base = NULL;
                        mvebu_pcie_powerdown(port);
                        continue;
index 597566f96f5eb68afba8b234c91ae990ba9340f6..1eeefa4df64c5059de0353f5de8f3ab4019fdc8a 100644 (file)
@@ -154,10 +154,11 @@ static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 static irqreturn_t rcar_pci_err_irq(int irq, void *pw)
 {
        struct rcar_pci_priv *priv = pw;
+       struct device *dev = priv->dev;
        u32 status = ioread32(priv->reg + RCAR_PCI_INT_STATUS_REG);
 
        if (status & RCAR_PCI_INT_ALLERRORS) {
-               dev_err(priv->dev, "error irq: status %08x\n", status);
+               dev_err(dev, "error irq: status %08x\n", status);
 
                /* clear the error(s) */
                iowrite32(status & RCAR_PCI_INT_ALLERRORS,
@@ -170,13 +171,14 @@ static irqreturn_t rcar_pci_err_irq(int irq, void *pw)
 
 static void rcar_pci_setup_errirq(struct rcar_pci_priv *priv)
 {
+       struct device *dev = priv->dev;
        int ret;
        u32 val;
 
-       ret = devm_request_irq(priv->dev, priv->irq, rcar_pci_err_irq,
+       ret = devm_request_irq(dev, priv->irq, rcar_pci_err_irq,
                               IRQF_SHARED, "error irq", priv);
        if (ret) {
-               dev_err(priv->dev, "cannot claim IRQ for error handling\n");
+               dev_err(dev, "cannot claim IRQ for error handling\n");
                return;
        }
 
@@ -192,15 +194,16 @@ static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { }
 static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
 {
        struct rcar_pci_priv *priv = sys->private_data;
+       struct device *dev = priv->dev;
        void __iomem *reg = priv->reg;
        u32 val;
        int ret;
 
-       pm_runtime_enable(priv->dev);
-       pm_runtime_get_sync(priv->dev);
+       pm_runtime_enable(dev);
+       pm_runtime_get_sync(dev);
 
        val = ioread32(reg + RCAR_PCI_UNIT_REV_REG);
-       dev_info(priv->dev, "PCI: bus%u revision %x\n", sys->busnr, val);
+       dev_info(dev, "PCI: bus%u revision %x\n", sys->busnr, val);
 
        /* Disable Direct Power Down State and assert reset */
        val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD;
@@ -275,7 +278,7 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
 
        /* Add PCI resources */
        pci_add_resource(&sys->resources, &priv->mem_res);
-       ret = devm_request_pci_bus_resources(priv->dev, &sys->resources);
+       ret = devm_request_pci_bus_resources(dev, &sys->resources);
        if (ret < 0)
                return ret;
 
@@ -311,6 +314,7 @@ static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
 static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci,
                                         struct device_node *np)
 {
+       struct device *dev = pci->dev;
        struct of_pci_range range;
        struct of_pci_range_parser parser;
        int index = 0;
@@ -331,14 +335,14 @@ static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci,
 
                /* Catch HW limitations */
                if (!(range.flags & IORESOURCE_PREFETCH)) {
-                       dev_err(pci->dev, "window must be prefetchable\n");
+                       dev_err(dev, "window must be prefetchable\n");
                        return -EINVAL;
                }
                if (pci->window_addr) {
                        u32 lowaddr = 1 << (ffs(pci->window_addr) - 1);
 
                        if (lowaddr < pci->window_size) {
-                               dev_err(pci->dev, "invalid window size/addr\n");
+                               dev_err(dev, "invalid window size/addr\n");
                                return -EINVAL;
                        }
                }
@@ -350,6 +354,7 @@ static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci,
 
 static int rcar_pci_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct resource *cfg_res, *mem_res;
        struct rcar_pci_priv *priv;
        void __iomem *reg;
@@ -357,7 +362,7 @@ static int rcar_pci_probe(struct platform_device *pdev)
        void *hw_private[1];
 
        cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       reg = devm_ioremap_resource(&pdev->dev, cfg_res);
+       reg = devm_ioremap_resource(dev, cfg_res);
        if (IS_ERR(reg))
                return PTR_ERR(reg);
 
@@ -368,8 +373,7 @@ static int rcar_pci_probe(struct platform_device *pdev)
        if (mem_res->start & 0xFFFF)
                return -EINVAL;
 
-       priv = devm_kzalloc(&pdev->dev,
-                           sizeof(struct rcar_pci_priv), GFP_KERNEL);
+       priv = devm_kzalloc(dev, sizeof(struct rcar_pci_priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
@@ -378,10 +382,10 @@ static int rcar_pci_probe(struct platform_device *pdev)
 
        priv->irq = platform_get_irq(pdev, 0);
        priv->reg = reg;
-       priv->dev = &pdev->dev;
+       priv->dev = dev;
 
        if (priv->irq < 0) {
-               dev_err(&pdev->dev, "no valid irq found\n");
+               dev_err(dev, "no valid irq found\n");
                return priv->irq;
        }
 
@@ -390,23 +394,23 @@ static int rcar_pci_probe(struct platform_device *pdev)
        priv->window_pci = 0x40000000;
        priv->window_size = SZ_1G;
 
-       if (pdev->dev.of_node) {
+       if (dev->of_node) {
                struct resource busnr;
                int ret;
 
-               ret = of_pci_parse_bus_range(pdev->dev.of_node, &busnr);
+               ret = of_pci_parse_bus_range(dev->of_node, &busnr);
                if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to parse bus-range\n");
+                       dev_err(dev, "failed to parse bus-range\n");
                        return ret;
                }
 
                priv->busnr = busnr.start;
                if (busnr.end != busnr.start)
-                       dev_warn(&pdev->dev, "only one bus number supported\n");
+                       dev_warn(dev, "only one bus number supported\n");
 
-               ret = rcar_pci_parse_map_dma_ranges(priv, pdev->dev.of_node);
+               ret = rcar_pci_parse_map_dma_ranges(priv, dev->of_node);
                if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to parse dma-range\n");
+                       dev_err(dev, "failed to parse dma-range\n");
                        return ret;
                }
        } else {
@@ -421,7 +425,7 @@ static int rcar_pci_probe(struct platform_device *pdev)
        hw.map_irq = rcar_pci_map_irq;
        hw.ops = &rcar_pci_ops;
        hw.setup = rcar_pci_setup;
-       pci_common_init_dev(&pdev->dev, &hw);
+       pci_common_init_dev(dev, &hw);
        return 0;
 }
 
index e2a8e4cab22eb37af1535092e3365315be1eee79..8dfccf7332411b379d262c165829efc3195ac21a 100644 (file)
@@ -384,6 +384,7 @@ static unsigned long tegra_pcie_conf_offset(unsigned int devfn, int where)
 static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
                                                   unsigned int busnr)
 {
+       struct device *dev = pcie->dev;
        pgprot_t prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
                                 L_PTE_XN | L_PTE_MT_DEV_SHARED | L_PTE_SHARED);
        phys_addr_t cs = pcie->cs->start;
@@ -413,8 +414,7 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
 
                err = ioremap_page_range(virt, virt + SZ_64K, phys, prot);
                if (err < 0) {
-                       dev_err(pcie->dev, "ioremap_page_range() failed: %d\n",
-                               err);
+                       dev_err(dev, "ioremap_page_range() failed: %d\n", err);
                        goto unmap;
                }
        }
@@ -462,6 +462,7 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus,
                                        int where)
 {
        struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
+       struct device *dev = pcie->dev;
        void __iomem *addr = NULL;
 
        if (bus->number == 0) {
@@ -482,8 +483,7 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus,
                                addr = (void __iomem *)b->area->addr;
 
                if (!addr) {
-                       dev_err(pcie->dev,
-                               "failed to map cfg. space for bus %u\n",
+                       dev_err(dev, "failed to map cfg. space for bus %u\n",
                                bus->number);
                        return NULL;
                }
@@ -584,12 +584,13 @@ static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
 static void tegra_pcie_port_free(struct tegra_pcie_port *port)
 {
        struct tegra_pcie *pcie = port->pcie;
+       struct device *dev = pcie->dev;
 
-       devm_iounmap(pcie->dev, port->base);
-       devm_release_mem_region(pcie->dev, port->regs.start,
+       devm_iounmap(dev, port->base);
+       devm_release_mem_region(dev, port->regs.start,
                                resource_size(&port->regs));
        list_del(&port->list);
-       devm_kfree(pcie->dev, port);
+       devm_kfree(dev, port);
 }
 
 /* Tegra PCIE root complex wrongly reports device class */
@@ -612,12 +613,13 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
 static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
 {
        struct tegra_pcie *pcie = sys_to_pcie(sys);
+       struct device *dev = pcie->dev;
        int err;
 
        sys->mem_offset = pcie->offset.mem;
        sys->io_offset = pcie->offset.io;
 
-       err = devm_request_resource(pcie->dev, &iomem_resource, &pcie->io);
+       err = devm_request_resource(dev, &iomem_resource, &pcie->io);
        if (err < 0)
                return err;
 
@@ -631,7 +633,7 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
                                sys->mem_offset);
        pci_add_resource(&sys->resources, &pcie->busn);
 
-       err = devm_request_pci_bus_resources(pcie->dev, &sys->resources);
+       err = devm_request_pci_bus_resources(dev, &sys->resources);
        if (err < 0)
                return err;
 
@@ -672,6 +674,7 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
                "Peer2Peer error",
        };
        struct tegra_pcie *pcie = arg;
+       struct device *dev = pcie->dev;
        u32 code, signature;
 
        code = afi_readl(pcie, AFI_INTR_CODE) & AFI_INTR_CODE_MASK;
@@ -689,11 +692,9 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
         * happen a lot during enumeration
         */
        if (code == AFI_INTR_MASTER_ABORT)
-               dev_dbg(pcie->dev, "%s, signature: %08x\n", err_msg[code],
-                       signature);
+               dev_dbg(dev, "%s, signature: %08x\n", err_msg[code], signature);
        else
-               dev_err(pcie->dev, "%s, signature: %08x\n", err_msg[code],
-                       signature);
+               dev_err(dev, "%s, signature: %08x\n", err_msg[code], signature);
 
        if (code == AFI_INTR_TARGET_ABORT || code == AFI_INTR_MASTER_ABORT ||
            code == AFI_INTR_FPCI_DECODE_ERROR) {
@@ -701,9 +702,9 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
                u64 address = (u64)fpci << 32 | (signature & 0xfffffffc);
 
                if (code == AFI_INTR_MASTER_ABORT)
-                       dev_dbg(pcie->dev, "  FPCI address: %10llx\n", address);
+                       dev_dbg(dev, "  FPCI address: %10llx\n", address);
                else
-                       dev_err(pcie->dev, "  FPCI address: %10llx\n", address);
+                       dev_err(dev, "  FPCI address: %10llx\n", address);
        }
 
        return IRQ_HANDLED;
@@ -793,6 +794,7 @@ static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout)
 
 static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        const struct tegra_pcie_soc *soc = pcie->soc;
        u32 value;
        int err;
@@ -829,7 +831,7 @@ static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
        /* wait for the PLL to lock */
        err = tegra_pcie_pll_wait(pcie, 500);
        if (err < 0) {
-               dev_err(pcie->dev, "PLL failed to lock: %d\n", err);
+               dev_err(dev, "PLL failed to lock: %d\n", err);
                return err;
        }
 
@@ -859,7 +861,7 @@ static int tegra_pcie_phy_disable(struct tegra_pcie *pcie)
        /* override IDDQ */
        value = pads_readl(pcie, PADS_CTL);
        value |= PADS_CTL_IDDQ_1L;
-       pads_writel(pcie, PADS_CTL, value);
+       pads_writel(pcie, value, PADS_CTL);
 
        /* reset PLL */
        value = pads_readl(pcie, soc->pads_pll_ctl);
@@ -880,8 +882,7 @@ static int tegra_pcie_port_phy_power_on(struct tegra_pcie_port *port)
        for (i = 0; i < port->lanes; i++) {
                err = phy_power_on(port->phys[i]);
                if (err < 0) {
-                       dev_err(dev, "failed to power on PHY#%u: %d\n", i,
-                               err);
+                       dev_err(dev, "failed to power on PHY#%u: %d\n", i, err);
                        return err;
                }
        }
@@ -909,6 +910,7 @@ static int tegra_pcie_port_phy_power_off(struct tegra_pcie_port *port)
 
 static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        const struct tegra_pcie_soc *soc = pcie->soc;
        struct tegra_pcie_port *port;
        int err;
@@ -920,7 +922,7 @@ static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie)
                        err = tegra_pcie_phy_enable(pcie);
 
                if (err < 0)
-                       dev_err(pcie->dev, "failed to power on PHY: %d\n", err);
+                       dev_err(dev, "failed to power on PHY: %d\n", err);
 
                return err;
        }
@@ -928,7 +930,7 @@ static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie)
        list_for_each_entry(port, &pcie->ports, list) {
                err = tegra_pcie_port_phy_power_on(port);
                if (err < 0) {
-                       dev_err(pcie->dev,
+                       dev_err(dev,
                                "failed to power on PCIe port %u PHY: %d\n",
                                port->index, err);
                        return err;
@@ -946,6 +948,7 @@ static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie)
 
 static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        struct tegra_pcie_port *port;
        int err;
 
@@ -956,8 +959,7 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie)
                        err = tegra_pcie_phy_disable(pcie);
 
                if (err < 0)
-                       dev_err(pcie->dev, "failed to power off PHY: %d\n",
-                               err);
+                       dev_err(dev, "failed to power off PHY: %d\n", err);
 
                return err;
        }
@@ -965,7 +967,7 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie)
        list_for_each_entry(port, &pcie->ports, list) {
                err = tegra_pcie_port_phy_power_off(port);
                if (err < 0) {
-                       dev_err(pcie->dev,
+                       dev_err(dev,
                                "failed to power off PCIe port %u PHY: %d\n",
                                port->index, err);
                        return err;
@@ -977,6 +979,7 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie)
 
 static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        const struct tegra_pcie_soc *soc = pcie->soc;
        struct tegra_pcie_port *port;
        unsigned long value;
@@ -1016,7 +1019,7 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
 
        err = tegra_pcie_phy_power_on(pcie);
        if (err < 0) {
-               dev_err(pcie->dev, "failed to power on PHY(s): %d\n", err);
+               dev_err(dev, "failed to power on PHY(s): %d\n", err);
                return err;
        }
 
@@ -1049,13 +1052,14 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
 
 static void tegra_pcie_power_off(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        int err;
 
        /* TODO: disable and unprepare clocks? */
 
        err = tegra_pcie_phy_power_off(pcie);
        if (err < 0)
-               dev_err(pcie->dev, "failed to power off PHY(s): %d\n", err);
+               dev_err(dev, "failed to power off PHY(s): %d\n", err);
 
        reset_control_assert(pcie->pcie_xrst);
        reset_control_assert(pcie->afi_rst);
@@ -1065,11 +1069,12 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
 
        err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies);
        if (err < 0)
-               dev_warn(pcie->dev, "failed to disable regulators: %d\n", err);
+               dev_warn(dev, "failed to disable regulators: %d\n", err);
 }
 
 static int tegra_pcie_power_on(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        const struct tegra_pcie_soc *soc = pcie->soc;
        int err;
 
@@ -1082,13 +1087,13 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
        /* enable regulators */
        err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies);
        if (err < 0)
-               dev_err(pcie->dev, "failed to enable regulators: %d\n", err);
+               dev_err(dev, "failed to enable regulators: %d\n", err);
 
        err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
                                                pcie->pex_clk,
                                                pcie->pex_rst);
        if (err) {
-               dev_err(pcie->dev, "powerup sequence failed: %d\n", err);
+               dev_err(dev, "powerup sequence failed: %d\n", err);
                return err;
        }
 
@@ -1096,22 +1101,21 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
 
        err = clk_prepare_enable(pcie->afi_clk);
        if (err < 0) {
-               dev_err(pcie->dev, "failed to enable AFI clock: %d\n", err);
+               dev_err(dev, "failed to enable AFI clock: %d\n", err);
                return err;
        }
 
        if (soc->has_cml_clk) {
                err = clk_prepare_enable(pcie->cml_clk);
                if (err < 0) {
-                       dev_err(pcie->dev, "failed to enable CML clock: %d\n",
-                               err);
+                       dev_err(dev, "failed to enable CML clock: %d\n", err);
                        return err;
                }
        }
 
        err = clk_prepare_enable(pcie->pll_e);
        if (err < 0) {
-               dev_err(pcie->dev, "failed to enable PLLE clock: %d\n", err);
+               dev_err(dev, "failed to enable PLLE clock: %d\n", err);
                return err;
        }
 
@@ -1120,22 +1124,23 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
 
 static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        const struct tegra_pcie_soc *soc = pcie->soc;
 
-       pcie->pex_clk = devm_clk_get(pcie->dev, "pex");
+       pcie->pex_clk = devm_clk_get(dev, "pex");
        if (IS_ERR(pcie->pex_clk))
                return PTR_ERR(pcie->pex_clk);
 
-       pcie->afi_clk = devm_clk_get(pcie->dev, "afi");
+       pcie->afi_clk = devm_clk_get(dev, "afi");
        if (IS_ERR(pcie->afi_clk))
                return PTR_ERR(pcie->afi_clk);
 
-       pcie->pll_e = devm_clk_get(pcie->dev, "pll_e");
+       pcie->pll_e = devm_clk_get(dev, "pll_e");
        if (IS_ERR(pcie->pll_e))
                return PTR_ERR(pcie->pll_e);
 
        if (soc->has_cml_clk) {
-               pcie->cml_clk = devm_clk_get(pcie->dev, "cml");
+               pcie->cml_clk = devm_clk_get(dev, "cml");
                if (IS_ERR(pcie->cml_clk))
                        return PTR_ERR(pcie->cml_clk);
        }
@@ -1145,15 +1150,17 @@ static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
 
 static int tegra_pcie_resets_get(struct tegra_pcie *pcie)
 {
-       pcie->pex_rst = devm_reset_control_get(pcie->dev, "pex");
+       struct device *dev = pcie->dev;
+
+       pcie->pex_rst = devm_reset_control_get(dev, "pex");
        if (IS_ERR(pcie->pex_rst))
                return PTR_ERR(pcie->pex_rst);
 
-       pcie->afi_rst = devm_reset_control_get(pcie->dev, "afi");
+       pcie->afi_rst = devm_reset_control_get(dev, "afi");
        if (IS_ERR(pcie->afi_rst))
                return PTR_ERR(pcie->afi_rst);
 
-       pcie->pcie_xrst = devm_reset_control_get(pcie->dev, "pcie_x");
+       pcie->pcie_xrst = devm_reset_control_get(dev, "pcie_x");
        if (IS_ERR(pcie->pcie_xrst))
                return PTR_ERR(pcie->pcie_xrst);
 
@@ -1162,18 +1169,19 @@ static int tegra_pcie_resets_get(struct tegra_pcie *pcie)
 
 static int tegra_pcie_phys_get_legacy(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        int err;
 
-       pcie->phy = devm_phy_optional_get(pcie->dev, "pcie");
+       pcie->phy = devm_phy_optional_get(dev, "pcie");
        if (IS_ERR(pcie->phy)) {
                err = PTR_ERR(pcie->phy);
-               dev_err(pcie->dev, "failed to get PHY: %d\n", err);
+               dev_err(dev, "failed to get PHY: %d\n", err);
                return err;
        }
 
        err = phy_init(pcie->phy);
        if (err < 0) {
-               dev_err(pcie->dev, "failed to initialize PHY: %d\n", err);
+               dev_err(dev, "failed to initialize PHY: %d\n", err);
                return err;
        }
 
@@ -1256,43 +1264,44 @@ static int tegra_pcie_phys_get(struct tegra_pcie *pcie)
 
 static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
 {
-       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct device *dev = pcie->dev;
+       struct platform_device *pdev = to_platform_device(dev);
        struct resource *pads, *afi, *res;
        int err;
 
        err = tegra_pcie_clocks_get(pcie);
        if (err) {
-               dev_err(&pdev->dev, "failed to get clocks: %d\n", err);
+               dev_err(dev, "failed to get clocks: %d\n", err);
                return err;
        }
 
        err = tegra_pcie_resets_get(pcie);
        if (err) {
-               dev_err(&pdev->dev, "failed to get resets: %d\n", err);
+               dev_err(dev, "failed to get resets: %d\n", err);
                return err;
        }
 
        err = tegra_pcie_phys_get(pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to get PHYs: %d\n", err);
+               dev_err(dev, "failed to get PHYs: %d\n", err);
                return err;
        }
 
        err = tegra_pcie_power_on(pcie);
        if (err) {
-               dev_err(&pdev->dev, "failed to power up: %d\n", err);
+               dev_err(dev, "failed to power up: %d\n", err);
                return err;
        }
 
        pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads");
-       pcie->pads = devm_ioremap_resource(&pdev->dev, pads);
+       pcie->pads = devm_ioremap_resource(dev, pads);
        if (IS_ERR(pcie->pads)) {
                err = PTR_ERR(pcie->pads);
                goto poweroff;
        }
 
        afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi");
-       pcie->afi = devm_ioremap_resource(&pdev->dev, afi);
+       pcie->afi = devm_ioremap_resource(dev, afi);
        if (IS_ERR(pcie->afi)) {
                err = PTR_ERR(pcie->afi);
                goto poweroff;
@@ -1305,7 +1314,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
                goto poweroff;
        }
 
-       pcie->cs = devm_request_mem_region(pcie->dev, res->start,
+       pcie->cs = devm_request_mem_region(dev, res->start,
                                           resource_size(res), res->name);
        if (!pcie->cs) {
                err = -EADDRNOTAVAIL;
@@ -1315,7 +1324,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
        /* request interrupt */
        err = platform_get_irq_byname(pdev, "intr");
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to get IRQ: %d\n", err);
+               dev_err(dev, "failed to get IRQ: %d\n", err);
                goto poweroff;
        }
 
@@ -1323,7 +1332,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
 
        err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie);
        if (err) {
-               dev_err(&pdev->dev, "failed to register IRQ: %d\n", err);
+               dev_err(dev, "failed to register IRQ: %d\n", err);
                goto poweroff;
        }
 
@@ -1336,6 +1345,7 @@ poweroff:
 
 static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        int err;
 
        if (pcie->irq > 0)
@@ -1345,7 +1355,7 @@ static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
 
        err = phy_exit(pcie->phy);
        if (err < 0)
-               dev_err(pcie->dev, "failed to teardown PHY: %d\n", err);
+               dev_err(dev, "failed to teardown PHY: %d\n", err);
 
        return 0;
 }
@@ -1384,6 +1394,7 @@ static void tegra_msi_free(struct tegra_msi *chip, unsigned long irq)
 static irqreturn_t tegra_pcie_msi_irq(int irq, void *data)
 {
        struct tegra_pcie *pcie = data;
+       struct device *dev = pcie->dev;
        struct tegra_msi *msi = &pcie->msi;
        unsigned int i, processed = 0;
 
@@ -1403,13 +1414,13 @@ static irqreturn_t tegra_pcie_msi_irq(int irq, void *data)
                                if (test_bit(index, msi->used))
                                        generic_handle_irq(irq);
                                else
-                                       dev_info(pcie->dev, "unhandled MSI\n");
+                                       dev_info(dev, "unhandled MSI\n");
                        } else {
                                /*
                                 * that's weird who triggered this?
                                 * just clear it
                                 */
-                               dev_info(pcie->dev, "unexpected MSI\n");
+                               dev_info(dev, "unexpected MSI\n");
                        }
 
                        /* see if there's any more pending in this vector */
@@ -1488,7 +1499,8 @@ static const struct irq_domain_ops msi_domain_ops = {
 
 static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
 {
-       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct device *dev = pcie->dev;
+       struct platform_device *pdev = to_platform_device(dev);
        const struct tegra_pcie_soc *soc = pcie->soc;
        struct tegra_msi *msi = &pcie->msi;
        unsigned long base;
@@ -1497,20 +1509,20 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
 
        mutex_init(&msi->lock);
 
-       msi->chip.dev = pcie->dev;
+       msi->chip.dev = dev;
        msi->chip.setup_irq = tegra_msi_setup_irq;
        msi->chip.teardown_irq = tegra_msi_teardown_irq;
 
-       msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR,
+       msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR,
                                            &msi_domain_ops, &msi->chip);
        if (!msi->domain) {
-               dev_err(&pdev->dev, "failed to create IRQ domain\n");
+               dev_err(dev, "failed to create IRQ domain\n");
                return -ENOMEM;
        }
 
        err = platform_get_irq_byname(pdev, "msi");
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to get IRQ: %d\n", err);
+               dev_err(dev, "failed to get IRQ: %d\n", err);
                goto err;
        }
 
@@ -1519,7 +1531,7 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
        err = request_irq(msi->irq, tegra_pcie_msi_irq, IRQF_NO_THREAD,
                          tegra_msi_irq_chip.name, pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+               dev_err(dev, "failed to request IRQ: %d\n", err);
                goto err;
        }
 
@@ -1594,46 +1606,47 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie)
 static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
                                      u32 *xbar)
 {
-       struct device_node *np = pcie->dev->of_node;
+       struct device *dev = pcie->dev;
+       struct device_node *np = dev->of_node;
 
        if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
                switch (lanes) {
                case 0x0000104:
-                       dev_info(pcie->dev, "4x1, 1x1 configuration\n");
+                       dev_info(dev, "4x1, 1x1 configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1;
                        return 0;
 
                case 0x0000102:
-                       dev_info(pcie->dev, "2x1, 1x1 configuration\n");
+                       dev_info(dev, "2x1, 1x1 configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1;
                        return 0;
                }
        } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
                switch (lanes) {
                case 0x00000204:
-                       dev_info(pcie->dev, "4x1, 2x1 configuration\n");
+                       dev_info(dev, "4x1, 2x1 configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420;
                        return 0;
 
                case 0x00020202:
-                       dev_info(pcie->dev, "2x3 configuration\n");
+                       dev_info(dev, "2x3 configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222;
                        return 0;
 
                case 0x00010104:
-                       dev_info(pcie->dev, "4x1, 1x2 configuration\n");
+                       dev_info(dev, "4x1, 1x2 configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411;
                        return 0;
                }
        } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
                switch (lanes) {
                case 0x00000004:
-                       dev_info(pcie->dev, "single-mode configuration\n");
+                       dev_info(dev, "single-mode configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE;
                        return 0;
 
                case 0x00000202:
-                       dev_info(pcie->dev, "dual-mode configuration\n");
+                       dev_info(dev, "dual-mode configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL;
                        return 0;
                }
@@ -1673,7 +1686,8 @@ static bool of_regulator_bulk_available(struct device_node *np,
  */
 static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
 {
-       struct device_node *np = pcie->dev->of_node;
+       struct device *dev = pcie->dev;
+       struct device_node *np = dev->of_node;
 
        if (of_device_is_compatible(np, "nvidia,tegra30-pcie"))
                pcie->num_supplies = 3;
@@ -1681,12 +1695,12 @@ static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
                pcie->num_supplies = 2;
 
        if (pcie->num_supplies == 0) {
-               dev_err(pcie->dev, "device %s not supported in legacy mode\n",
+               dev_err(dev, "device %s not supported in legacy mode\n",
                        np->full_name);
                return -ENODEV;
        }
 
-       pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+       pcie->supplies = devm_kcalloc(dev, pcie->num_supplies,
                                      sizeof(*pcie->supplies),
                                      GFP_KERNEL);
        if (!pcie->supplies)
@@ -1698,8 +1712,7 @@ static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
        if (pcie->num_supplies > 2)
                pcie->supplies[2].supply = "avdd";
 
-       return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
-                                      pcie->supplies);
+       return devm_regulator_bulk_get(dev, pcie->num_supplies, pcie->supplies);
 }
 
 /*
@@ -1713,13 +1726,14 @@ static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
  */
 static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
 {
-       struct device_node *np = pcie->dev->of_node;
+       struct device *dev = pcie->dev;
+       struct device_node *np = dev->of_node;
        unsigned int i = 0;
 
        if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
                pcie->num_supplies = 7;
 
-               pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+               pcie->supplies = devm_kcalloc(dev, pcie->num_supplies,
                                              sizeof(*pcie->supplies),
                                              GFP_KERNEL);
                if (!pcie->supplies)
@@ -1746,7 +1760,7 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
                pcie->num_supplies = 4 + (need_pexa ? 2 : 0) +
                                         (need_pexb ? 2 : 0);
 
-               pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+               pcie->supplies = devm_kcalloc(dev, pcie->num_supplies,
                                              sizeof(*pcie->supplies),
                                              GFP_KERNEL);
                if (!pcie->supplies)
@@ -1769,7 +1783,7 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
        } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
                pcie->num_supplies = 5;
 
-               pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+               pcie->supplies = devm_kcalloc(dev, pcie->num_supplies,
                                              sizeof(*pcie->supplies),
                                              GFP_KERNEL);
                if (!pcie->supplies)
@@ -1782,9 +1796,9 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
                pcie->supplies[4].supply = "vddio-pex-clk";
        }
 
-       if (of_regulator_bulk_available(pcie->dev->of_node, pcie->supplies,
+       if (of_regulator_bulk_available(dev->of_node, pcie->supplies,
                                        pcie->num_supplies))
-               return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
+               return devm_regulator_bulk_get(dev, pcie->num_supplies,
                                               pcie->supplies);
 
        /*
@@ -1792,9 +1806,9 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
         * that the device tree complies with an older version of the device
         * tree binding.
         */
-       dev_info(pcie->dev, "using legacy DT binding for power supplies\n");
+       dev_info(dev, "using legacy DT binding for power supplies\n");
 
-       devm_kfree(pcie->dev, pcie->supplies);
+       devm_kfree(dev, pcie->supplies);
        pcie->num_supplies = 0;
 
        return tegra_pcie_get_legacy_regulators(pcie);
@@ -1802,7 +1816,8 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
 
 static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 {
-       struct device_node *np = pcie->dev->of_node, *port;
+       struct device *dev = pcie->dev;
+       struct device_node *np = dev->of_node, *port;
        const struct tegra_pcie_soc *soc = pcie->soc;
        struct of_pci_range_parser parser;
        struct of_pci_range range;
@@ -1812,7 +1827,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
        int err;
 
        if (of_pci_range_parser_init(&parser, np)) {
-               dev_err(pcie->dev, "missing \"ranges\" property\n");
+               dev_err(dev, "missing \"ranges\" property\n");
                return -EINVAL;
        }
 
@@ -1867,8 +1882,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 
        err = of_pci_parse_bus_range(np, &pcie->busn);
        if (err < 0) {
-               dev_err(pcie->dev, "failed to parse ranges property: %d\n",
-                       err);
+               dev_err(dev, "failed to parse ranges property: %d\n", err);
                pcie->busn.name = np->name;
                pcie->busn.start = 0;
                pcie->busn.end = 0xff;
@@ -1883,15 +1897,14 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 
                err = of_pci_get_devfn(port);
                if (err < 0) {
-                       dev_err(pcie->dev, "failed to parse address: %d\n",
-                               err);
+                       dev_err(dev, "failed to parse address: %d\n", err);
                        return err;
                }
 
                index = PCI_SLOT(err);
 
                if (index < 1 || index > soc->num_ports) {
-                       dev_err(pcie->dev, "invalid port number: %d\n", index);
+                       dev_err(dev, "invalid port number: %d\n", index);
                        return -EINVAL;
                }
 
@@ -1899,13 +1912,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 
                err = of_property_read_u32(port, "nvidia,num-lanes", &value);
                if (err < 0) {
-                       dev_err(pcie->dev, "failed to parse # of lanes: %d\n",
+                       dev_err(dev, "failed to parse # of lanes: %d\n",
                                err);
                        return err;
                }
 
                if (value > 16) {
-                       dev_err(pcie->dev, "invalid # of lanes: %u\n", value);
+                       dev_err(dev, "invalid # of lanes: %u\n", value);
                        return -EINVAL;
                }
 
@@ -1919,14 +1932,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
                mask |= ((1 << value) - 1) << lane;
                lane += value;
 
-               rp = devm_kzalloc(pcie->dev, sizeof(*rp), GFP_KERNEL);
+               rp = devm_kzalloc(dev, sizeof(*rp), GFP_KERNEL);
                if (!rp)
                        return -ENOMEM;
 
                err = of_address_to_resource(port, 0, &rp->regs);
                if (err < 0) {
-                       dev_err(pcie->dev, "failed to parse address: %d\n",
-                               err);
+                       dev_err(dev, "failed to parse address: %d\n", err);
                        return err;
                }
 
@@ -1936,7 +1948,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
                rp->pcie = pcie;
                rp->np = port;
 
-               rp->base = devm_ioremap_resource(pcie->dev, &rp->regs);
+               rp->base = devm_ioremap_resource(dev, &rp->regs);
                if (IS_ERR(rp->base))
                        return PTR_ERR(rp->base);
 
@@ -1945,7 +1957,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 
        err = tegra_pcie_get_xbar_config(pcie, lanes, &pcie->xbar_config);
        if (err < 0) {
-               dev_err(pcie->dev, "invalid lane configuration\n");
+               dev_err(dev, "invalid lane configuration\n");
                return err;
        }
 
@@ -1964,6 +1976,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 #define TEGRA_PCIE_LINKUP_TIMEOUT      200     /* up to 1.2 seconds */
 static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
 {
+       struct device *dev = port->pcie->dev;
        unsigned int retries = 3;
        unsigned long value;
 
@@ -1986,8 +1999,7 @@ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
                } while (--timeout);
 
                if (!timeout) {
-                       dev_err(port->pcie->dev, "link %u down, retrying\n",
-                               port->index);
+                       dev_err(dev, "link %u down, retrying\n", port->index);
                        goto retry;
                }
 
@@ -2011,11 +2023,12 @@ retry:
 
 static int tegra_pcie_enable(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        struct tegra_pcie_port *port, *tmp;
        struct hw_pci hw;
 
        list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
-               dev_info(pcie->dev, "probing port %u, using %u lanes\n",
+               dev_info(dev, "probing port %u, using %u lanes\n",
                         port->index, port->lanes);
 
                tegra_pcie_port_enable(port);
@@ -2023,7 +2036,7 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
                if (tegra_pcie_port_check_link(port))
                        continue;
 
-               dev_info(pcie->dev, "link %u down, ignoring\n", port->index);
+               dev_info(dev, "link %u down, ignoring\n", port->index);
 
                tegra_pcie_port_disable(port);
                tegra_pcie_port_free(port);
@@ -2041,8 +2054,7 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
        hw.map_irq = tegra_pcie_map_irq;
        hw.ops = &tegra_pcie_ops;
 
-       pci_common_init_dev(pcie->dev, &hw);
-
+       pci_common_init_dev(dev, &hw);
        return 0;
 }
 
@@ -2204,17 +2216,18 @@ remove:
 
 static int tegra_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct tegra_pcie *pcie;
        int err;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
-       pcie->soc = of_device_get_match_data(&pdev->dev);
+       pcie->soc = of_device_get_match_data(dev);
        INIT_LIST_HEAD(&pcie->buses);
        INIT_LIST_HEAD(&pcie->ports);
-       pcie->dev = &pdev->dev;
+       pcie->dev = dev;
 
        err = tegra_pcie_parse_dt(pcie);
        if (err < 0)
@@ -2222,7 +2235,7 @@ static int tegra_pcie_probe(struct platform_device *pdev)
 
        err = tegra_pcie_get_resources(pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to request resources: %d\n", err);
+               dev_err(dev, "failed to request resources: %d\n", err);
                return err;
        }
 
@@ -2236,27 +2249,23 @@ static int tegra_pcie_probe(struct platform_device *pdev)
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                err = tegra_pcie_enable_msi(pcie);
                if (err < 0) {
-                       dev_err(&pdev->dev,
-                               "failed to enable MSI support: %d\n",
-                               err);
+                       dev_err(dev, "failed to enable MSI support: %d\n", err);
                        goto put_resources;
                }
        }
 
        err = tegra_pcie_enable(pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to enable PCIe ports: %d\n", err);
+               dev_err(dev, "failed to enable PCIe ports: %d\n", err);
                goto disable_msi;
        }
 
        if (IS_ENABLED(CONFIG_DEBUG_FS)) {
                err = tegra_pcie_debugfs_init(pcie);
                if (err < 0)
-                       dev_err(&pdev->dev, "failed to setup debugfs: %d\n",
-                               err);
+                       dev_err(dev, "failed to setup debugfs: %d\n", err);
        }
 
-       platform_set_drvdata(pdev, pcie);
        return 0;
 
 disable_msi:
index a81273c23341a4738617a5db2473ae682a30388d..1de23d74783f88d4a3dc59cd69810c172b7bf5cc 100644 (file)
@@ -76,6 +76,16 @@ struct xgene_pcie_port {
        u32                     version;
 };
 
+static u32 xgene_pcie_readl(struct xgene_pcie_port *port, u32 reg)
+{
+       return readl(port->csr_base + reg);
+}
+
+static void xgene_pcie_writel(struct xgene_pcie_port *port, u32 reg, u32 val)
+{
+       writel(val, port->csr_base + reg);
+}
+
 static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
 {
        return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags;
@@ -112,9 +122,9 @@ static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
        if (!pci_is_root_bus(bus))
                rtdid_val = (b << 8) | (d << 3) | f;
 
-       writel(rtdid_val, port->csr_base + RTDID);
+       xgene_pcie_writel(port, RTDID, rtdid_val);
        /* read the register back to ensure flush */
-       readl(port->csr_base + RTDID);
+       xgene_pcie_readl(port, RTDID);
 }
 
 /*
@@ -179,28 +189,28 @@ static struct pci_ops xgene_pcie_ops = {
        .write = pci_generic_config_write32,
 };
 
-static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr,
+static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr,
                                  u32 flags, u64 size)
 {
        u64 mask = (~(size - 1) & PCI_BASE_ADDRESS_MEM_MASK) | flags;
        u32 val32 = 0;
        u32 val;
 
-       val32 = readl(csr_base + addr);
+       val32 = xgene_pcie_readl(port, addr);
        val = (val32 & 0x0000ffff) | (lower_32_bits(mask) << 16);
-       writel(val, csr_base + addr);
+       xgene_pcie_writel(port, addr, val);
 
-       val32 = readl(csr_base + addr + 0x04);
+       val32 = xgene_pcie_readl(port, addr + 0x04);
        val = (val32 & 0xffff0000) | (lower_32_bits(mask) >> 16);
-       writel(val, csr_base + addr + 0x04);
+       xgene_pcie_writel(port, addr + 0x04, val);
 
-       val32 = readl(csr_base + addr + 0x04);
+       val32 = xgene_pcie_readl(port, addr + 0x04);
        val = (val32 & 0x0000ffff) | (upper_32_bits(mask) << 16);
-       writel(val, csr_base + addr + 0x04);
+       xgene_pcie_writel(port, addr + 0x04, val);
 
-       val32 = readl(csr_base + addr + 0x08);
+       val32 = xgene_pcie_readl(port, addr + 0x08);
        val = (val32 & 0xffff0000) | (upper_32_bits(mask) >> 16);
-       writel(val, csr_base + addr + 0x08);
+       xgene_pcie_writel(port, addr + 0x08, val);
 
        return mask;
 }
@@ -208,32 +218,32 @@ static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr,
 static void xgene_pcie_linkup(struct xgene_pcie_port *port,
                                   u32 *lanes, u32 *speed)
 {
-       void __iomem *csr_base = port->csr_base;
        u32 val32;
 
        port->link_up = false;
-       val32 = readl(csr_base + PCIECORE_CTLANDSTATUS);
+       val32 = xgene_pcie_readl(port, PCIECORE_CTLANDSTATUS);
        if (val32 & LINK_UP_MASK) {
                port->link_up = true;
                *speed = PIPE_PHY_RATE_RD(val32);
-               val32 = readl(csr_base + BRIDGE_STATUS_0);
+               val32 = xgene_pcie_readl(port, BRIDGE_STATUS_0);
                *lanes = val32 >> 26;
        }
 }
 
 static int xgene_pcie_init_port(struct xgene_pcie_port *port)
 {
+       struct device *dev = port->dev;
        int rc;
 
-       port->clk = clk_get(port->dev, NULL);
+       port->clk = clk_get(dev, NULL);
        if (IS_ERR(port->clk)) {
-               dev_err(port->dev, "clock not available\n");
+               dev_err(dev, "clock not available\n");
                return -ENODEV;
        }
 
        rc = clk_prepare_enable(port->clk);
        if (rc) {
-               dev_err(port->dev, "clock enable failed\n");
+               dev_err(dev, "clock enable failed\n");
                return rc;
        }
 
@@ -243,15 +253,16 @@ static int xgene_pcie_init_port(struct xgene_pcie_port *port)
 static int xgene_pcie_map_reg(struct xgene_pcie_port *port,
                              struct platform_device *pdev)
 {
+       struct device *dev = port->dev;
        struct resource *res;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
-       port->csr_base = devm_ioremap_resource(port->dev, res);
+       port->csr_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(port->csr_base))
                return PTR_ERR(port->csr_base);
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
-       port->cfg_base = devm_ioremap_resource(port->dev, res);
+       port->cfg_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(port->cfg_base))
                return PTR_ERR(port->cfg_base);
        port->cfg_addr = res->start;
@@ -263,7 +274,7 @@ static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port,
                                    struct resource *res, u32 offset,
                                    u64 cpu_addr, u64 pci_addr)
 {
-       void __iomem *base = port->csr_base + offset;
+       struct device *dev = port->dev;
        resource_size_t size = resource_size(res);
        u64 restype = resource_type(res);
        u64 mask = 0;
@@ -280,22 +291,24 @@ static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port,
        if (size >= min_size)
                mask = ~(size - 1) | flag;
        else
-               dev_warn(port->dev, "res size 0x%llx less than minimum 0x%x\n",
+               dev_warn(dev, "res size 0x%llx less than minimum 0x%x\n",
                         (u64)size, min_size);
 
-       writel(lower_32_bits(cpu_addr), base);
-       writel(upper_32_bits(cpu_addr), base + 0x04);
-       writel(lower_32_bits(mask), base + 0x08);
-       writel(upper_32_bits(mask), base + 0x0c);
-       writel(lower_32_bits(pci_addr), base + 0x10);
-       writel(upper_32_bits(pci_addr), base + 0x14);
+       xgene_pcie_writel(port, offset, lower_32_bits(cpu_addr));
+       xgene_pcie_writel(port, offset + 0x04, upper_32_bits(cpu_addr));
+       xgene_pcie_writel(port, offset + 0x08, lower_32_bits(mask));
+       xgene_pcie_writel(port, offset + 0x0c, upper_32_bits(mask));
+       xgene_pcie_writel(port, offset + 0x10, lower_32_bits(pci_addr));
+       xgene_pcie_writel(port, offset + 0x14, upper_32_bits(pci_addr));
 }
 
-static void xgene_pcie_setup_cfg_reg(void __iomem *csr_base, u64 addr)
+static void xgene_pcie_setup_cfg_reg(struct xgene_pcie_port *port)
 {
-       writel(lower_32_bits(addr), csr_base + CFGBARL);
-       writel(upper_32_bits(addr), csr_base + CFGBARH);
-       writel(EN_REG, csr_base + CFGCTL);
+       u64 addr = port->cfg_addr;
+
+       xgene_pcie_writel(port, CFGBARL, lower_32_bits(addr));
+       xgene_pcie_writel(port, CFGBARH, upper_32_bits(addr));
+       xgene_pcie_writel(port, CFGCTL, EN_REG);
 }
 
 static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
@@ -310,7 +323,7 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
                struct resource *res = window->res;
                u64 restype = resource_type(res);
 
-               dev_dbg(port->dev, "%pR\n", res);
+               dev_dbg(dev, "%pR\n", res);
 
                switch (restype) {
                case IORESOURCE_IO:
@@ -339,17 +352,18 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
                        return -EINVAL;
                }
        }
-       xgene_pcie_setup_cfg_reg(port->csr_base, port->cfg_addr);
-
+       xgene_pcie_setup_cfg_reg(port);
        return 0;
 }
 
-static void xgene_pcie_setup_pims(void *addr, u64 pim, u64 size)
+static void xgene_pcie_setup_pims(struct xgene_pcie_port *port, u32 pim_reg,
+                                 u64 pim, u64 size)
 {
-       writel(lower_32_bits(pim), addr);
-       writel(upper_32_bits(pim) | EN_COHERENCY, addr + 0x04);
-       writel(lower_32_bits(size), addr + 0x10);
-       writel(upper_32_bits(size), addr + 0x14);
+       xgene_pcie_writel(port, pim_reg, lower_32_bits(pim));
+       xgene_pcie_writel(port, pim_reg + 0x04,
+                         upper_32_bits(pim) | EN_COHERENCY);
+       xgene_pcie_writel(port, pim_reg + 0x10, lower_32_bits(size));
+       xgene_pcie_writel(port, pim_reg + 0x14, upper_32_bits(size));
 }
 
 /*
@@ -379,10 +393,10 @@ static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size)
 static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
                                    struct of_pci_range *range, u8 *ib_reg_mask)
 {
-       void __iomem *csr_base = port->csr_base;
        void __iomem *cfg_base = port->cfg_base;
+       struct device *dev = port->dev;
        void *bar_addr;
-       void *pim_addr;
+       u32 pim_reg;
        u64 cpu_addr = range->cpu_addr;
        u64 pci_addr = range->pci_addr;
        u64 size = range->size;
@@ -393,7 +407,7 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
 
        region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size);
        if (region < 0) {
-               dev_warn(port->dev, "invalid pcie dma-range config\n");
+               dev_warn(dev, "invalid pcie dma-range config\n");
                return;
        }
 
@@ -403,29 +417,27 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
        bar_low = pcie_bar_low_val((u32)cpu_addr, flags);
        switch (region) {
        case 0:
-               xgene_pcie_set_ib_mask(csr_base, BRIDGE_CFG_4, flags, size);
+               xgene_pcie_set_ib_mask(port, BRIDGE_CFG_4, flags, size);
                bar_addr = cfg_base + PCI_BASE_ADDRESS_0;
                writel(bar_low, bar_addr);
                writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
-               pim_addr = csr_base + PIM1_1L;
+               pim_reg = PIM1_1L;
                break;
        case 1:
-               bar_addr = csr_base + IBAR2;
-               writel(bar_low, bar_addr);
-               writel(lower_32_bits(mask), csr_base + IR2MSK);
-               pim_addr = csr_base + PIM2_1L;
+               xgene_pcie_writel(port, IBAR2, bar_low);
+               xgene_pcie_writel(port, IR2MSK, lower_32_bits(mask));
+               pim_reg = PIM2_1L;
                break;
        case 2:
-               bar_addr = csr_base + IBAR3L;
-               writel(bar_low, bar_addr);
-               writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
-               writel(lower_32_bits(mask), csr_base + IR3MSKL);
-               writel(upper_32_bits(mask), csr_base + IR3MSKL + 0x4);
-               pim_addr = csr_base + PIM3_1L;
+               xgene_pcie_writel(port, IBAR3L, bar_low);
+               xgene_pcie_writel(port, IBAR3L + 0x4, upper_32_bits(cpu_addr));
+               xgene_pcie_writel(port, IR3MSKL, lower_32_bits(mask));
+               xgene_pcie_writel(port, IR3MSKL + 0x4, upper_32_bits(mask));
+               pim_reg = PIM3_1L;
                break;
        }
 
-       xgene_pcie_setup_pims(pim_addr, pci_addr, ~(size - 1));
+       xgene_pcie_setup_pims(port, pim_reg, pci_addr, ~(size - 1));
 }
 
 static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
@@ -463,7 +475,7 @@ static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port)
        for_each_of_pci_range(&parser, &range) {
                u64 end = range.cpu_addr + range.size - 1;
 
-               dev_dbg(port->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
+               dev_dbg(dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
                        range.flags, range.cpu_addr, end, range.pci_addr);
                xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask);
        }
@@ -476,13 +488,14 @@ static void xgene_pcie_clear_config(struct xgene_pcie_port *port)
        int i;
 
        for (i = PIM1_1L; i <= CFGCTL; i += 4)
-               writel(0x0, port->csr_base + i);
+               xgene_pcie_writel(port, i, 0);
 }
 
 static int xgene_pcie_setup(struct xgene_pcie_port *port,
                            struct list_head *res,
                            resource_size_t io_base)
 {
+       struct device *dev = port->dev;
        u32 val, lanes = 0, speed = 0;
        int ret;
 
@@ -490,7 +503,7 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 
        /* setup the vendor and device IDs correctly */
        val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID;
-       writel(val, port->csr_base + BRIDGE_CFG_0);
+       xgene_pcie_writel(port, BRIDGE_CFG_0, val);
 
        ret = xgene_pcie_map_ranges(port, res, io_base);
        if (ret)
@@ -502,27 +515,28 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 
        xgene_pcie_linkup(port, &lanes, &speed);
        if (!port->link_up)
-               dev_info(port->dev, "(rc) link down\n");
+               dev_info(dev, "(rc) link down\n");
        else
-               dev_info(port->dev, "(rc) x%d gen-%d link up\n",
-                               lanes, speed + 1);
+               dev_info(dev, "(rc) x%d gen-%d link up\n", lanes, speed + 1);
        return 0;
 }
 
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
-       struct device_node *dn = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct device_node *dn = dev->of_node;
        struct xgene_pcie_port *port;
        resource_size_t iobase = 0;
        struct pci_bus *bus;
        int ret;
        LIST_HEAD(res);
 
-       port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
+       port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
        if (!port)
                return -ENOMEM;
-       port->node = of_node_get(pdev->dev.of_node);
-       port->dev = &pdev->dev;
+
+       port->node = of_node_get(dn);
+       port->dev = dev;
 
        port->version = XGENE_PCIE_IP_VER_UNKN;
        if (of_device_is_compatible(port->node, "apm,xgene-pcie"))
@@ -540,7 +554,7 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       ret = devm_request_pci_bus_resources(&pdev->dev, &res);
+       ret = devm_request_pci_bus_resources(dev, &res);
        if (ret)
                goto error;
 
@@ -548,8 +562,7 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (ret)
                goto error;
 
-       bus = pci_create_root_bus(&pdev->dev, 0,
-                                       &xgene_pcie_ops, port, &res);
+       bus = pci_create_root_bus(dev, 0, &xgene_pcie_ops, port, &res);
        if (!bus) {
                ret = -ENOMEM;
                goto error;
@@ -558,8 +571,6 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
-
-       platform_set_drvdata(pdev, port);
        return 0;
 
 error:
index c24e96559cbbb8fa0a894e9a07b039cb85d34e50..b0ac4dfafa0bbb116bfc80d754ac612c197171be 100644 (file)
 #define TLP_PAYLOAD_SIZE               0x01
 #define TLP_READ_TAG                   0x1d
 #define TLP_WRITE_TAG                  0x10
-#define TLP_CFG_DW0(fmttype)           (((fmttype) << 24) | TLP_PAYLOAD_SIZE)
-#define TLP_CFG_DW1(reqid, tag, be)    (((reqid) << 16) | (tag << 8) | (be))
+#define RP_DEVFN                       0
+#define TLP_REQ_ID(bus, devfn)         (((bus) << 8) | (devfn))
+#define TLP_CFG_DW0(pcie, bus)                                         \
+    ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0                 \
+                                   : TLP_FMTTYPE_CFGRD1) << 24) |      \
+     TLP_PAYLOAD_SIZE)
+#define TLP_CFG_DW1(pcie, tag, be)     \
+    (((TLP_REQ_ID(pcie->root_bus_nr,  RP_DEVFN)) << 16) | (tag << 8) | (be))
 #define TLP_CFG_DW2(bus, devfn, offset)        \
                                (((bus) << 24) | ((devfn) << 16) | (offset))
-#define TLP_REQ_ID(bus, devfn)         (((bus) << 8) | (devfn))
 #define TLP_COMP_STATUS(s)             (((s) >> 12) & 7)
 #define TLP_HDR_SIZE                   3
 #define TLP_LOOP                       500
-#define RP_DEVFN                       0
 
 #define LINK_UP_TIMEOUT                        HZ
 #define LINK_RETRAIN_TIMEOUT           HZ
@@ -74,7 +78,7 @@
 
 struct altera_pcie {
        struct platform_device  *pdev;
-       void __iomem            *cra_base;
+       void __iomem            *cra_base;      /* DT Cra */
        int                     irq;
        u8                      root_bus_nr;
        struct irq_domain       *irq_domain;
@@ -131,7 +135,7 @@ static void tlp_write_tx(struct altera_pcie *pcie,
        cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL);
 }
 
-static bool altera_pcie_valid_config(struct altera_pcie *pcie,
+static bool altera_pcie_valid_device(struct altera_pcie *pcie,
                                     struct pci_bus *bus, int dev)
 {
        /* If there is no link, then there is no device */
@@ -218,13 +222,8 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
 {
        u32 headers[TLP_HDR_SIZE];
 
-       if (bus == pcie->root_bus_nr)
-               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD0);
-       else
-               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD1);
-
-       headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN),
-                                       TLP_READ_TAG, byte_en);
+       headers[0] = TLP_CFG_DW0(pcie, bus);
+       headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en);
        headers[2] = TLP_CFG_DW2(bus, devfn, where);
 
        tlp_write_packet(pcie, headers, 0, false);
@@ -238,13 +237,8 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
        u32 headers[TLP_HDR_SIZE];
        int ret;
 
-       if (bus == pcie->root_bus_nr)
-               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR0);
-       else
-               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR1);
-
-       headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN),
-                                       TLP_WRITE_TAG, byte_en);
+       headers[0] = TLP_CFG_DW0(pcie, bus);
+       headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en);
        headers[2] = TLP_CFG_DW2(bus, devfn, where);
 
        /* check alignment to Qword */
@@ -342,7 +336,7 @@ static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
        if (altera_pcie_hide_rc_bar(bus, devfn, where))
                return PCIBIOS_BAD_REGISTER_NUMBER;
 
-       if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
+       if (!altera_pcie_valid_device(pcie, bus, PCI_SLOT(devfn))) {
                *value = 0xffffffff;
                return PCIBIOS_DEVICE_NOT_FOUND;
        }
@@ -359,7 +353,7 @@ static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
        if (altera_pcie_hide_rc_bar(bus, devfn, where))
                return PCIBIOS_BAD_REGISTER_NUMBER;
 
-       if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
+       if (!altera_pcie_valid_device(pcie, bus, PCI_SLOT(devfn)))
                return PCIBIOS_DEVICE_NOT_FOUND;
 
        return _altera_pcie_cfg_write(pcie, bus->number, devfn, where, size,
@@ -394,6 +388,7 @@ static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno,
 
 static void altera_wait_link_retrain(struct altera_pcie *pcie)
 {
+       struct device *dev = &pcie->pdev->dev;
        u16 reg16;
        unsigned long start_jiffies;
 
@@ -406,7 +401,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie)
                        break;
 
                if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) {
-                       dev_err(&pcie->pdev->dev, "link retrain timeout\n");
+                       dev_err(dev, "link retrain timeout\n");
                        break;
                }
                udelay(100);
@@ -419,7 +414,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie)
                        break;
 
                if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) {
-                       dev_err(&pcie->pdev->dev, "link up timeout\n");
+                       dev_err(dev, "link up timeout\n");
                        break;
                }
                udelay(100);
@@ -460,7 +455,6 @@ static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
 {
        irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
        irq_set_chip_data(irq, domain->host_data);
-
        return 0;
 }
 
@@ -472,12 +466,14 @@ static void altera_pcie_isr(struct irq_desc *desc)
 {
        struct irq_chip *chip = irq_desc_get_chip(desc);
        struct altera_pcie *pcie;
+       struct device *dev;
        unsigned long status;
        u32 bit;
        u32 virq;
 
        chained_irq_enter(chip, desc);
        pcie = irq_desc_get_handler_data(desc);
+       dev = &pcie->pdev->dev;
 
        while ((status = cra_readl(pcie, P2A_INT_STATUS)
                & P2A_INT_STS_ALL) != 0) {
@@ -489,8 +485,7 @@ static void altera_pcie_isr(struct irq_desc *desc)
                        if (virq)
                                generic_handle_irq(virq);
                        else
-                               dev_err(&pcie->pdev->dev,
-                                       "unexpected IRQ, INT%d\n", bit);
+                               dev_err(dev, "unexpected IRQ, INT%d\n", bit);
                }
        }
 
@@ -549,30 +544,25 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
 
 static int altera_pcie_parse_dt(struct altera_pcie *pcie)
 {
-       struct resource *cra;
+       struct device *dev = &pcie->pdev->dev;
        struct platform_device *pdev = pcie->pdev;
+       struct resource *cra;
 
        cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra");
-       if (!cra) {
-               dev_err(&pdev->dev, "no Cra memory resource defined\n");
-               return -ENODEV;
-       }
-
-       pcie->cra_base = devm_ioremap_resource(&pdev->dev, cra);
+       pcie->cra_base = devm_ioremap_resource(dev, cra);
        if (IS_ERR(pcie->cra_base)) {
-               dev_err(&pdev->dev, "failed to map cra memory\n");
+               dev_err(dev, "failed to map cra memory\n");
                return PTR_ERR(pcie->cra_base);
        }
 
        /* setup IRQ */
        pcie->irq = platform_get_irq(pdev, 0);
        if (pcie->irq <= 0) {
-               dev_err(&pdev->dev, "failed to get IRQ: %d\n", pcie->irq);
+               dev_err(dev, "failed to get IRQ: %d\n", pcie->irq);
                return -EINVAL;
        }
 
        irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie);
-
        return 0;
 }
 
@@ -583,12 +573,13 @@ static void altera_pcie_host_init(struct altera_pcie *pcie)
 
 static int altera_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct altera_pcie *pcie;
        struct pci_bus *bus;
        struct pci_bus *child;
        int ret;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
@@ -596,7 +587,7 @@ static int altera_pcie_probe(struct platform_device *pdev)
 
        ret = altera_pcie_parse_dt(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Parsing DT failed\n");
+               dev_err(dev, "Parsing DT failed\n");
                return ret;
        }
 
@@ -604,13 +595,13 @@ static int altera_pcie_probe(struct platform_device *pdev)
 
        ret = altera_pcie_parse_request_of_pci_ranges(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed add resources\n");
+               dev_err(dev, "Failed add resources\n");
                return ret;
        }
 
        ret = altera_pcie_init_irq_domain(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed creating IRQ Domain\n");
+               dev_err(dev, "Failed creating IRQ Domain\n");
                return ret;
        }
 
@@ -620,7 +611,7 @@ static int altera_pcie_probe(struct platform_device *pdev)
        cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE);
        altera_pcie_host_init(pcie);
 
-       bus = pci_scan_root_bus(&pdev->dev, pcie->root_bus_nr, &altera_pcie_ops,
+       bus = pci_scan_root_bus(dev, pcie->root_bus_nr, &altera_pcie_ops,
                                pcie, &pcie->resources);
        if (!bus)
                return -ENOMEM;
@@ -633,8 +624,6 @@ static int altera_pcie_probe(struct platform_device *pdev)
                pcie_bus_configure_settings(child);
 
        pci_bus_add_devices(bus);
-
-       platform_set_drvdata(pdev, pcie);
        return ret;
 }
 
index 0f4f570068e3d74f4e4aff4ed78f6bc356f7c319..0ac0f18690f20a6743193cb9714feaf72f309487 100644 (file)
 #include "pcie-designware.h"
 
 struct armada8k_pcie {
-       void __iomem *base;
+       struct pcie_port pp;            /* pp.dbi_base is DT ctrl */
        struct clk *clk;
-       struct pcie_port pp;
 };
 
 #define PCIE_VENDOR_REGS_OFFSET                0x8000
 
-#define PCIE_GLOBAL_CONTROL_REG                0x0
+#define PCIE_GLOBAL_CONTROL_REG                (PCIE_VENDOR_REGS_OFFSET + 0x0)
 #define PCIE_APP_LTSSM_EN              BIT(2)
 #define PCIE_DEVICE_TYPE_SHIFT         4
 #define PCIE_DEVICE_TYPE_MASK          0xF
 #define PCIE_DEVICE_TYPE_RC            0x4 /* Root complex */
 
-#define PCIE_GLOBAL_STATUS_REG         0x8
+#define PCIE_GLOBAL_STATUS_REG         (PCIE_VENDOR_REGS_OFFSET + 0x8)
 #define PCIE_GLB_STS_RDLH_LINK_UP      BIT(1)
 #define PCIE_GLB_STS_PHY_LINK_UP       BIT(9)
 
-#define PCIE_GLOBAL_INT_CAUSE1_REG     0x1C
-#define PCIE_GLOBAL_INT_MASK1_REG      0x20
+#define PCIE_GLOBAL_INT_CAUSE1_REG     (PCIE_VENDOR_REGS_OFFSET + 0x1C)
+#define PCIE_GLOBAL_INT_MASK1_REG      (PCIE_VENDOR_REGS_OFFSET + 0x20)
 #define PCIE_INT_A_ASSERT_MASK         BIT(9)
 #define PCIE_INT_B_ASSERT_MASK         BIT(10)
 #define PCIE_INT_C_ASSERT_MASK         BIT(11)
 #define PCIE_INT_D_ASSERT_MASK         BIT(12)
 
-#define PCIE_ARCACHE_TRC_REG           0x50
-#define PCIE_AWCACHE_TRC_REG           0x54
-#define PCIE_ARUSER_REG                        0x5C
-#define PCIE_AWUSER_REG                        0x60
+#define PCIE_ARCACHE_TRC_REG           (PCIE_VENDOR_REGS_OFFSET + 0x50)
+#define PCIE_AWCACHE_TRC_REG           (PCIE_VENDOR_REGS_OFFSET + 0x54)
+#define PCIE_ARUSER_REG                        (PCIE_VENDOR_REGS_OFFSET + 0x5C)
+#define PCIE_AWUSER_REG                        (PCIE_VENDOR_REGS_OFFSET + 0x60)
 /*
  * AR/AW Cache defauls: Normal memory, Write-Back, Read / Write
  * allocate
@@ -72,11 +71,10 @@ struct armada8k_pcie {
 
 static int armada8k_pcie_link_up(struct pcie_port *pp)
 {
-       struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
        u32 reg;
        u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP;
 
-       reg = readl(pcie->base + PCIE_GLOBAL_STATUS_REG);
+       reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_STATUS_REG);
 
        if ((reg & mask) == mask)
                return 1;
@@ -85,51 +83,50 @@ static int armada8k_pcie_link_up(struct pcie_port *pp)
        return 0;
 }
 
-static void armada8k_pcie_establish_link(struct pcie_port *pp)
+static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie)
 {
-       struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
-       void __iomem *base = pcie->base;
+       struct pcie_port *pp = &pcie->pp;
        u32 reg;
 
        if (!dw_pcie_link_up(pp)) {
                /* Disable LTSSM state machine to enable configuration */
-               reg = readl(base + PCIE_GLOBAL_CONTROL_REG);
+               reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG);
                reg &= ~(PCIE_APP_LTSSM_EN);
-               writel(reg, base + PCIE_GLOBAL_CONTROL_REG);
+               dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg);
        }
 
        /* Set the device to root complex mode */
-       reg = readl(base + PCIE_GLOBAL_CONTROL_REG);
+       reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG);
        reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT);
        reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT;
-       writel(reg, base + PCIE_GLOBAL_CONTROL_REG);
+       dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg);
 
        /* Set the PCIe master AxCache attributes */
-       writel(ARCACHE_DEFAULT_VALUE, base + PCIE_ARCACHE_TRC_REG);
-       writel(AWCACHE_DEFAULT_VALUE, base + PCIE_AWCACHE_TRC_REG);
+       dw_pcie_writel_rc(pp, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE);
+       dw_pcie_writel_rc(pp, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE);
 
        /* Set the PCIe master AxDomain attributes */
-       reg = readl(base + PCIE_ARUSER_REG);
+       reg = dw_pcie_readl_rc(pp, PCIE_ARUSER_REG);
        reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
        reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
-       writel(reg, base + PCIE_ARUSER_REG);
+       dw_pcie_writel_rc(pp, PCIE_ARUSER_REG, reg);
 
-       reg = readl(base + PCIE_AWUSER_REG);
+       reg = dw_pcie_readl_rc(pp, PCIE_AWUSER_REG);
        reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
        reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
-       writel(reg, base + PCIE_AWUSER_REG);
+       dw_pcie_writel_rc(pp, PCIE_AWUSER_REG, reg);
 
        /* Enable INT A-D interrupts */
-       reg = readl(base + PCIE_GLOBAL_INT_MASK1_REG);
+       reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_INT_MASK1_REG);
        reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK |
               PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK;
-       writel(reg, base + PCIE_GLOBAL_INT_MASK1_REG);
+       dw_pcie_writel_rc(pp, PCIE_GLOBAL_INT_MASK1_REG, reg);
 
        if (!dw_pcie_link_up(pp)) {
                /* Configuration done. Start LTSSM */
-               reg = readl(base + PCIE_GLOBAL_CONTROL_REG);
+               reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG);
                reg |= PCIE_APP_LTSSM_EN;
-               writel(reg, base + PCIE_GLOBAL_CONTROL_REG);
+               dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg);
        }
 
        /* Wait until the link becomes active again */
@@ -139,15 +136,16 @@ static void armada8k_pcie_establish_link(struct pcie_port *pp)
 
 static void armada8k_pcie_host_init(struct pcie_port *pp)
 {
+       struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
+
        dw_pcie_setup_rc(pp);
-       armada8k_pcie_establish_link(pp);
+       armada8k_pcie_establish_link(pcie);
 }
 
 static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
-       struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
-       void __iomem *base = pcie->base;
+       struct armada8k_pcie *pcie = arg;
+       struct pcie_port *pp = &pcie->pp;
        u32 val;
 
        /*
@@ -155,8 +153,8 @@ static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
         * PCI device. However, they are also latched into the PCIe
         * controller, so we simply discard them.
         */
-       val = readl(base + PCIE_GLOBAL_INT_CAUSE1_REG);
-       writel(val, base + PCIE_GLOBAL_INT_CAUSE1_REG);
+       val = dw_pcie_readl_rc(pp, PCIE_GLOBAL_INT_CAUSE1_REG);
+       dw_pcie_writel_rc(pp, PCIE_GLOBAL_INT_CAUSE1_REG, val);
 
        return IRQ_HANDLED;
 }
@@ -166,9 +164,10 @@ static struct pcie_host_ops armada8k_pcie_host_ops = {
        .host_init = armada8k_pcie_host_init,
 };
 
-static int armada8k_add_pcie_port(struct pcie_port *pp,
+static int armada8k_add_pcie_port(struct armada8k_pcie *pcie,
                                  struct platform_device *pdev)
 {
+       struct pcie_port *pp = &pcie->pp;
        struct device *dev = &pdev->dev;
        int ret;
 
@@ -182,7 +181,7 @@ static int armada8k_add_pcie_port(struct pcie_port *pp,
        }
 
        ret = devm_request_irq(dev, pp->irq, armada8k_pcie_irq_handler,
-                              IRQF_SHARED, "armada8k-pcie", pp);
+                              IRQF_SHARED, "armada8k-pcie", pcie);
        if (ret) {
                dev_err(dev, "failed to request irq %d\n", pp->irq);
                return ret;
@@ -217,7 +216,6 @@ static int armada8k_pcie_probe(struct platform_device *pdev)
 
        pp = &pcie->pp;
        pp->dev = dev;
-       platform_set_drvdata(pdev, pcie);
 
        /* Get the dw-pcie unit configuration/control registers base. */
        base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
@@ -228,9 +226,7 @@ static int armada8k_pcie_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       pcie->base = pp->dbi_base + PCIE_VENDOR_REGS_OFFSET;
-
-       ret = armada8k_add_pcie_port(pp, pdev);
+       ret = armada8k_add_pcie_port(pcie, pdev);
        if (ret)
                goto fail;
 
index 39bf1a6df4631de2532c84fc340b841ca6744eeb..212786b27f1a1b1f9ed9a07ba5c410617e2569c0 100644 (file)
@@ -27,9 +27,9 @@
 #define to_artpec6_pcie(x)     container_of(x, struct artpec6_pcie, pp)
 
 struct artpec6_pcie {
-       struct pcie_port        pp;
-       struct regmap           *regmap;
-       void __iomem            *phy_base;
+       struct pcie_port        pp;             /* pp.dbi_base is DT dbi */
+       struct regmap           *regmap;        /* DT axis,syscon-pcie */
+       void __iomem            *phy_base;      /* DT phy */
 };
 
 /* PCIe Port Logic registers (memory-mapped) */
@@ -65,18 +65,31 @@ struct artpec6_pcie {
 
 #define ARTPEC6_CPU_TO_BUS_ADDR                0x0fffffff
 
-static int artpec6_pcie_establish_link(struct pcie_port *pp)
+static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset)
 {
-       struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pp);
+       u32 val;
+
+       regmap_read(artpec6_pcie->regmap, offset, &val);
+       return val;
+}
+
+static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u32 val)
+{
+       regmap_write(artpec6_pcie->regmap, offset, val);
+}
+
+static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
+{
+       struct pcie_port *pp = &artpec6_pcie->pp;
        u32 val;
        unsigned int retries;
 
        /* Hold DW core in reset */
-       regmap_read(artpec6_pcie->regmap, PCIECFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
        val |= PCIECFG_CORE_RESET_REQ;
-       regmap_write(artpec6_pcie->regmap, PCIECFG, val);
+       artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
 
-       regmap_read(artpec6_pcie->regmap, PCIECFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
        val |=  PCIECFG_RISRCREN |      /* Receiver term. 50 Ohm */
                PCIECFG_MODE_TX_DRV_EN |
                PCIECFG_CISRREN |       /* Reference clock term. 100 Ohm */
@@ -84,27 +97,27 @@ static int artpec6_pcie_establish_link(struct pcie_port *pp)
        val |= PCIECFG_REFCLK_ENABLE;
        val &= ~PCIECFG_DBG_OEN;
        val &= ~PCIECFG_CLKREQ_B;
-       regmap_write(artpec6_pcie->regmap, PCIECFG, val);
+       artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
        usleep_range(5000, 6000);
 
-       regmap_read(artpec6_pcie->regmap, NOCCFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
        val |= NOCCFG_ENABLE_CLK_PCIE;
-       regmap_write(artpec6_pcie->regmap, NOCCFG, val);
+       artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
        usleep_range(20, 30);
 
-       regmap_read(artpec6_pcie->regmap, PCIECFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
        val |= PCIECFG_PCLK_ENABLE | PCIECFG_PLL_ENABLE;
-       regmap_write(artpec6_pcie->regmap, PCIECFG, val);
+       artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
        usleep_range(6000, 7000);
 
-       regmap_read(artpec6_pcie->regmap, NOCCFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
        val &= ~NOCCFG_POWER_PCIE_IDLEREQ;
-       regmap_write(artpec6_pcie->regmap, NOCCFG, val);
+       artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
 
        retries = 50;
        do {
                usleep_range(1000, 2000);
-               regmap_read(artpec6_pcie->regmap, NOCCFG, &val);
+               val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
                retries--;
        } while (retries &&
                (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE)));
@@ -117,16 +130,16 @@ static int artpec6_pcie_establish_link(struct pcie_port *pp)
        } while (retries && !(val & PHY_COSPLLLOCK));
 
        /* Take DW core out of reset */
-       regmap_read(artpec6_pcie->regmap, PCIECFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
        val &= ~PCIECFG_CORE_RESET_REQ;
-       regmap_write(artpec6_pcie->regmap, PCIECFG, val);
+       artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
        usleep_range(100, 200);
 
        /*
         * Enable writing to config regs. This is required as the Synopsys
         * driver changes the class code. That register needs DBI write enable.
         */
-       writel(DBI_RO_WR_EN, pp->dbi_base + MISC_CONTROL_1_OFF);
+       dw_pcie_writel_rc(pp, MISC_CONTROL_1_OFF, DBI_RO_WR_EN);
 
        pp->io_base &= ARTPEC6_CPU_TO_BUS_ADDR;
        pp->mem_base &= ARTPEC6_CPU_TO_BUS_ADDR;
@@ -137,78 +150,69 @@ static int artpec6_pcie_establish_link(struct pcie_port *pp)
        dw_pcie_setup_rc(pp);
 
        /* assert LTSSM enable */
-       regmap_read(artpec6_pcie->regmap, PCIECFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
        val |= PCIECFG_LTSSM_ENABLE;
-       regmap_write(artpec6_pcie->regmap, PCIECFG, val);
+       artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
 
        /* check if the link is up or not */
        if (!dw_pcie_wait_for_link(pp))
                return 0;
 
        dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
 
        return -ETIMEDOUT;
 }
 
-static void artpec6_pcie_enable_interrupts(struct pcie_port *pp)
+static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie)
 {
+       struct pcie_port *pp = &artpec6_pcie->pp;
+
        if (IS_ENABLED(CONFIG_PCI_MSI))
                dw_pcie_msi_init(pp);
 }
 
 static void artpec6_pcie_host_init(struct pcie_port *pp)
 {
-       artpec6_pcie_establish_link(pp);
-       artpec6_pcie_enable_interrupts(pp);
-}
-
-static int artpec6_pcie_link_up(struct pcie_port *pp)
-{
-       u32 rc;
-
-       /*
-        * Get status from Synopsys IP
-        * link is debug bit 36, debug register 1 starts at bit 32
-        */
-       rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << (36 - 32));
-       if (rc)
-               return 1;
+       struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pp);
 
-       return 0;
+       artpec6_pcie_establish_link(artpec6_pcie);
+       artpec6_pcie_enable_interrupts(artpec6_pcie);
 }
 
 static struct pcie_host_ops artpec6_pcie_host_ops = {
-       .link_up = artpec6_pcie_link_up,
        .host_init = artpec6_pcie_host_init,
 };
 
 static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
+       struct artpec6_pcie *artpec6_pcie = arg;
+       struct pcie_port *pp = &artpec6_pcie->pp;
 
        return dw_handle_msi_irq(pp);
 }
 
-static int artpec6_add_pcie_port(struct pcie_port *pp,
+static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
                                 struct platform_device *pdev)
 {
+       struct pcie_port *pp = &artpec6_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                pp->msi_irq = platform_get_irq_byname(pdev, "msi");
                if (pp->msi_irq <= 0) {
-                       dev_err(&pdev->dev, "failed to get MSI irq\n");
+                       dev_err(dev, "failed to get MSI irq\n");
                        return -ENODEV;
                }
 
-               ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+               ret = devm_request_irq(dev, pp->msi_irq,
                                       artpec6_pcie_msi_handler,
                                       IRQF_SHARED | IRQF_NO_THREAD,
-                                      "artpec6-pcie-msi", pp);
+                                      "artpec6-pcie-msi", artpec6_pcie);
                if (ret) {
-                       dev_err(&pdev->dev, "failed to request MSI irq\n");
+                       dev_err(dev, "failed to request MSI irq\n");
                        return ret;
                }
        }
@@ -218,7 +222,7 @@ static int artpec6_add_pcie_port(struct pcie_port *pp,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -227,41 +231,40 @@ static int artpec6_add_pcie_port(struct pcie_port *pp,
 
 static int artpec6_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct artpec6_pcie *artpec6_pcie;
        struct pcie_port *pp;
        struct resource *dbi_base;
        struct resource *phy_base;
        int ret;
 
-       artpec6_pcie = devm_kzalloc(&pdev->dev, sizeof(*artpec6_pcie),
-                                   GFP_KERNEL);
+       artpec6_pcie = devm_kzalloc(dev, sizeof(*artpec6_pcie), GFP_KERNEL);
        if (!artpec6_pcie)
                return -ENOMEM;
 
        pp = &artpec6_pcie->pp;
-       pp->dev = &pdev->dev;
+       pp->dev = dev;
 
        dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
-       pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base);
+       pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
        if (IS_ERR(pp->dbi_base))
                return PTR_ERR(pp->dbi_base);
 
        phy_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
-       artpec6_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy_base);
+       artpec6_pcie->phy_base = devm_ioremap_resource(dev, phy_base);
        if (IS_ERR(artpec6_pcie->phy_base))
                return PTR_ERR(artpec6_pcie->phy_base);
 
        artpec6_pcie->regmap =
-               syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+               syscon_regmap_lookup_by_phandle(dev->of_node,
                                                "axis,syscon-pcie");
        if (IS_ERR(artpec6_pcie->regmap))
                return PTR_ERR(artpec6_pcie->regmap);
 
-       ret = artpec6_add_pcie_port(pp, pdev);
+       ret = artpec6_add_pcie_port(artpec6_pcie, pdev);
        if (ret < 0)
                return ret;
 
-       platform_set_drvdata(pdev, artpec6_pcie);
        return 0;
 }
 
index 17da005497a5cd5f402b95a9c28a291860783f28..537f58a664fa230d3f6496ff68f4e7495d0f5952 100644 (file)
@@ -25,8 +25,7 @@
 #include "pcie-designware.h"
 
 struct dw_plat_pcie {
-       void __iomem            *mem_base;
-       struct pcie_port        pp;
+       struct pcie_port        pp;     /* pp.dbi_base is DT 0th resource */
 };
 
 static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
@@ -52,6 +51,7 @@ static struct pcie_host_ops dw_plat_pcie_host_ops = {
 static int dw_plat_add_pcie_port(struct pcie_port *pp,
                                 struct platform_device *pdev)
 {
+       struct device *dev = pp->dev;
        int ret;
 
        pp->irq = platform_get_irq(pdev, 1);
@@ -63,11 +63,11 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp,
                if (pp->msi_irq < 0)
                        return pp->msi_irq;
 
-               ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+               ret = devm_request_irq(dev, pp->msi_irq,
                                        dw_plat_pcie_msi_irq_handler,
                                        IRQF_SHARED, "dw-plat-pcie-msi", pp);
                if (ret) {
-                       dev_err(&pdev->dev, "failed to request MSI IRQ\n");
+                       dev_err(dev, "failed to request MSI IRQ\n");
                        return ret;
                }
        }
@@ -77,7 +77,7 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -86,31 +86,28 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp,
 
 static int dw_plat_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct dw_plat_pcie *dw_plat_pcie;
        struct pcie_port *pp;
        struct resource *res;  /* Resource from DT */
        int ret;
 
-       dw_plat_pcie = devm_kzalloc(&pdev->dev, sizeof(*dw_plat_pcie),
-                                       GFP_KERNEL);
+       dw_plat_pcie = devm_kzalloc(dev, sizeof(*dw_plat_pcie), GFP_KERNEL);
        if (!dw_plat_pcie)
                return -ENOMEM;
 
        pp = &dw_plat_pcie->pp;
-       pp->dev = &pdev->dev;
+       pp->dev = dev;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       dw_plat_pcie->mem_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(dw_plat_pcie->mem_base))
-               return PTR_ERR(dw_plat_pcie->mem_base);
-
-       pp->dbi_base = dw_plat_pcie->mem_base;
+       pp->dbi_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pp->dbi_base))
+               return PTR_ERR(pp->dbi_base);
 
        ret = dw_plat_add_pcie_port(pp, pdev);
        if (ret < 0)
                return ret;
 
-       platform_set_drvdata(pdev, dw_plat_pcie);
        return 0;
 }
 
index 74da71ea544a91bc369dfa17151e1bf9b7fb747d..035f50c03281c803f82d721c0a3c3a2c10a4a348 100644 (file)
@@ -141,41 +141,35 @@ int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val)
        return PCIBIOS_SUCCESSFUL;
 }
 
-static inline u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg)
+u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg)
 {
        if (pp->ops->readl_rc)
-               return pp->ops->readl_rc(pp, pp->dbi_base + reg);
+               return pp->ops->readl_rc(pp, reg);
 
        return readl(pp->dbi_base + reg);
 }
 
-static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
+void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val)
 {
        if (pp->ops->writel_rc)
-               pp->ops->writel_rc(pp, val, pp->dbi_base + reg);
+               pp->ops->writel_rc(pp, reg, val);
        else
                writel(val, pp->dbi_base + reg);
 }
 
-static inline u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg)
+static u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg)
 {
        u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
 
-       if (pp->ops->readl_rc)
-               return pp->ops->readl_rc(pp, pp->dbi_base + offset + reg);
-
-       return readl(pp->dbi_base + offset + reg);
+       return dw_pcie_readl_rc(pp, offset + reg);
 }
 
-static inline void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index,
-                                        u32 val, u32 reg)
+static void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index, u32 reg,
+                                 u32 val)
 {
        u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
 
-       if (pp->ops->writel_rc)
-               pp->ops->writel_rc(pp, val, pp->dbi_base + offset + reg);
-       else
-               writel(val, pp->dbi_base + offset + reg);
+       dw_pcie_writel_rc(pp, offset + reg, val);
 }
 
 static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
@@ -202,35 +196,35 @@ static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
        u32 retries, val;
 
        if (pp->iatu_unroll_enabled) {
-               dw_pcie_writel_unroll(pp, index,
-                       lower_32_bits(cpu_addr), PCIE_ATU_UNR_LOWER_BASE);
-               dw_pcie_writel_unroll(pp, index,
-                       upper_32_bits(cpu_addr), PCIE_ATU_UNR_UPPER_BASE);
-               dw_pcie_writel_unroll(pp, index,
-                       lower_32_bits(cpu_addr + size - 1), PCIE_ATU_UNR_LIMIT);
-               dw_pcie_writel_unroll(pp, index,
-                       lower_32_bits(pci_addr), PCIE_ATU_UNR_LOWER_TARGET);
-               dw_pcie_writel_unroll(pp, index,
-                       upper_32_bits(pci_addr), PCIE_ATU_UNR_UPPER_TARGET);
-               dw_pcie_writel_unroll(pp, index,
-                       type, PCIE_ATU_UNR_REGION_CTRL1);
-               dw_pcie_writel_unroll(pp, index,
-                       PCIE_ATU_ENABLE, PCIE_ATU_UNR_REGION_CTRL2);
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_BASE,
+                       lower_32_bits(cpu_addr));
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_UPPER_BASE,
+                       upper_32_bits(cpu_addr));
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LIMIT,
+                       lower_32_bits(cpu_addr + size - 1));
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_TARGET,
+                       lower_32_bits(pci_addr));
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_UPPER_TARGET,
+                       upper_32_bits(pci_addr));
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_REGION_CTRL1,
+                       type);
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_REGION_CTRL2,
+                       PCIE_ATU_ENABLE);
        } else {
-               dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
-                                               PCIE_ATU_VIEWPORT);
-               dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr),
-                                               PCIE_ATU_LOWER_BASE);
-               dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr),
-                                               PCIE_ATU_UPPER_BASE);
-               dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
-                                               PCIE_ATU_LIMIT);
-               dw_pcie_writel_rc(pp, lower_32_bits(pci_addr),
-                                               PCIE_ATU_LOWER_TARGET);
-               dw_pcie_writel_rc(pp, upper_32_bits(pci_addr),
-                                               PCIE_ATU_UPPER_TARGET);
-               dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
-               dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+               dw_pcie_writel_rc(pp, PCIE_ATU_VIEWPORT,
+                                 PCIE_ATU_REGION_OUTBOUND | index);
+               dw_pcie_writel_rc(pp, PCIE_ATU_LOWER_BASE,
+                                 lower_32_bits(cpu_addr));
+               dw_pcie_writel_rc(pp, PCIE_ATU_UPPER_BASE,
+                                 upper_32_bits(cpu_addr));
+               dw_pcie_writel_rc(pp, PCIE_ATU_LIMIT,
+                                 lower_32_bits(cpu_addr + size - 1));
+               dw_pcie_writel_rc(pp, PCIE_ATU_LOWER_TARGET,
+                                 lower_32_bits(pci_addr));
+               dw_pcie_writel_rc(pp, PCIE_ATU_UPPER_TARGET,
+                                 upper_32_bits(pci_addr));
+               dw_pcie_writel_rc(pp, PCIE_ATU_CR1, type);
+               dw_pcie_writel_rc(pp, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
        }
 
        /*
@@ -760,8 +754,8 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        return ret;
 }
 
-static int dw_pcie_valid_config(struct pcie_port *pp,
-                               struct pci_bus *bus, int dev)
+static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus,
+                               int dev)
 {
        /* If there is no link, then there is no device */
        if (bus->number != pp->root_bus_nr) {
@@ -781,7 +775,7 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
 {
        struct pcie_port *pp = bus->sysdata;
 
-       if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
+       if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) {
                *val = 0xffffffff;
                return PCIBIOS_DEVICE_NOT_FOUND;
        }
@@ -797,7 +791,7 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 {
        struct pcie_port *pp = bus->sysdata;
 
-       if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
+       if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn)))
                return PCIBIOS_DEVICE_NOT_FOUND;
 
        if (bus->number == pp->root_bus_nr)
@@ -835,7 +829,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
                dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes);
                return;
        }
-       dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
+       dw_pcie_writel_rc(pp, PCIE_PORT_LINK_CONTROL, val);
 
        /* set link width speed control register */
        val = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
@@ -854,30 +848,30 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
                val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
                break;
        }
-       dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL);
+       dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
 
        /* setup RC BARs */
-       dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0);
-       dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 0x00000004);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_1, 0x00000000);
 
        /* setup interrupt pins */
        val = dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE);
        val &= 0xffff00ff;
        val |= 0x00000100;
-       dw_pcie_writel_rc(pp, val, PCI_INTERRUPT_LINE);
+       dw_pcie_writel_rc(pp, PCI_INTERRUPT_LINE, val);
 
        /* setup bus numbers */
        val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS);
        val &= 0xff000000;
        val |= 0x00010100;
-       dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
+       dw_pcie_writel_rc(pp, PCI_PRIMARY_BUS, val);
 
        /* setup command register */
        val = dw_pcie_readl_rc(pp, PCI_COMMAND);
        val &= 0xffff0000;
        val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
                PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
-       dw_pcie_writel_rc(pp, val, PCI_COMMAND);
+       dw_pcie_writel_rc(pp, PCI_COMMAND, val);
 
        /*
         * If the platform provides ->rd_other_conf, it means the platform
index c8e5bc647f4958d0cf66643d8280bc7f41713ab7..a567ea288ee2f9fa7e12483e5feb64853029990b 100644 (file)
@@ -54,9 +54,8 @@ struct pcie_port {
 };
 
 struct pcie_host_ops {
-       u32 (*readl_rc)(struct pcie_port *pp, void __iomem *dbi_base);
-       void (*writel_rc)(struct pcie_port *pp,
-                       u32 val, void __iomem *dbi_base);
+       u32 (*readl_rc)(struct pcie_port *pp, u32 reg);
+       void (*writel_rc)(struct pcie_port *pp, u32 reg, u32 val);
        int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
        int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val);
        int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus,
@@ -73,6 +72,8 @@ struct pcie_host_ops {
        int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip);
 };
 
+u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg);
+void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val);
 int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
 int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
index 7ee9dfcc45fb79b7fad6a4ab1f9df7b80fdecd9c..56154c25980c6d83b1a155fc07e7520e8cee3ac1 100644 (file)
 
 #include "pcie-designware.h"
 
-#define PCIE_LTSSM_LINKUP_STATE                                0x11
-#define PCIE_LTSSM_STATE_MASK                          0x3F
-#define PCIE_SUBCTRL_SYS_STATE4_REG                    0x6818
-#define PCIE_SYS_STATE4                                                0x31c
-#define PCIE_HIP06_CTRL_OFF                                    0x1000
+#define PCIE_SUBCTRL_SYS_STATE4_REG            0x6818
+#define PCIE_HIP06_CTRL_OFF                    0x1000
+#define PCIE_SYS_STATE4                                (PCIE_HIP06_CTRL_OFF + 0x31c)
+#define PCIE_LTSSM_LINKUP_STATE                        0x11
+#define PCIE_LTSSM_STATE_MASK                  0x3F
 
 #define to_hisi_pcie(x)        container_of(x, struct hisi_pcie, pp)
 
 struct hisi_pcie;
 
 struct pcie_soc_ops {
-       int (*hisi_pcie_link_up)(struct hisi_pcie *pcie);
+       int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie);
 };
 
 struct hisi_pcie {
+       struct pcie_port pp;            /* pp.dbi_base is DT rc_dbi */
        struct regmap *subctrl;
-       void __iomem *reg_base;
        u32 port_id;
-       struct pcie_port pp;
        struct pcie_soc_ops *soc_ops;
 };
 
-static inline void hisi_pcie_apb_writel(struct hisi_pcie *pcie,
-                                       u32 val, u32 reg)
-{
-       writel(val, pcie->reg_base + reg);
-}
-
-static inline u32 hisi_pcie_apb_readl(struct hisi_pcie *pcie, u32 reg)
-{
-       return readl(pcie->reg_base + reg);
-}
-
 /* HipXX PCIe host only supports 32-bit config access */
 static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
                              u32 *val)
 {
        u32 reg;
        u32 reg_val;
-       struct hisi_pcie *pcie = to_hisi_pcie(pp);
        void *walker = &reg_val;
 
        walker += (where & 0x3);
        reg = where & ~0x3;
-       reg_val = hisi_pcie_apb_readl(pcie, reg);
+       reg_val = dw_pcie_readl_rc(pp, reg);
 
        if (size == 1)
                *val = *(u8 __force *) walker;
@@ -86,21 +73,20 @@ static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int  size,
 {
        u32 reg_val;
        u32 reg;
-       struct hisi_pcie *pcie = to_hisi_pcie(pp);
        void *walker = &reg_val;
 
        walker += (where & 0x3);
        reg = where & ~0x3;
        if (size == 4)
-               hisi_pcie_apb_writel(pcie, val, reg);
+               dw_pcie_writel_rc(pp, reg, val);
        else if (size == 2) {
-               reg_val = hisi_pcie_apb_readl(pcie, reg);
+               reg_val = dw_pcie_readl_rc(pp, reg);
                *(u16 __force *) walker = val;
-               hisi_pcie_apb_writel(pcie, reg_val, reg);
+               dw_pcie_writel_rc(pp, reg, reg_val);
        } else if (size == 1) {
-               reg_val = hisi_pcie_apb_readl(pcie, reg);
+               reg_val = dw_pcie_readl_rc(pp, reg);
                *(u8 __force *) walker = val;
-               hisi_pcie_apb_writel(pcie, reg_val, reg);
+               dw_pcie_writel_rc(pp, reg, reg_val);
        } else
                return PCIBIOS_BAD_REGISTER_NUMBER;
 
@@ -119,10 +105,10 @@ static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie)
 
 static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie)
 {
+       struct pcie_port *pp = &hisi_pcie->pp;
        u32 val;
 
-       val = hisi_pcie_apb_readl(hisi_pcie, PCIE_HIP06_CTRL_OFF +
-                       PCIE_SYS_STATE4);
+       val = dw_pcie_readl_rc(pp, PCIE_SYS_STATE4);
 
        return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
 }
@@ -140,19 +126,20 @@ static struct pcie_host_ops hisi_pcie_host_ops = {
        .link_up = hisi_pcie_link_up,
 };
 
-static int hisi_add_pcie_port(struct pcie_port *pp,
-                                    struct platform_device *pdev)
+static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie,
+                             struct platform_device *pdev)
 {
+       struct pcie_port *pp = &hisi_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
        u32 port_id;
-       struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
 
-       if (of_property_read_u32(pdev->dev.of_node, "port-id", &port_id)) {
-               dev_err(&pdev->dev, "failed to read port-id\n");
+       if (of_property_read_u32(dev->of_node, "port-id", &port_id)) {
+               dev_err(dev, "failed to read port-id\n");
                return -EINVAL;
        }
        if (port_id > 3) {
-               dev_err(&pdev->dev, "Invalid port-id: %d\n", port_id);
+               dev_err(dev, "Invalid port-id: %d\n", port_id);
                return -EINVAL;
        }
        hisi_pcie->port_id = port_id;
@@ -161,7 +148,7 @@ static int hisi_add_pcie_port(struct pcie_port *pp,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -170,6 +157,7 @@ static int hisi_add_pcie_port(struct pcie_port *pp,
 
 static int hisi_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct hisi_pcie *hisi_pcie;
        struct pcie_port *pp;
        const struct of_device_id *match;
@@ -177,40 +165,36 @@ static int hisi_pcie_probe(struct platform_device *pdev)
        struct device_driver *driver;
        int ret;
 
-       hisi_pcie = devm_kzalloc(&pdev->dev, sizeof(*hisi_pcie), GFP_KERNEL);
+       hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL);
        if (!hisi_pcie)
                return -ENOMEM;
 
        pp = &hisi_pcie->pp;
-       pp->dev = &pdev->dev;
-       driver = (pdev->dev).driver;
+       pp->dev = dev;
+       driver = dev->driver;
 
-       match = of_match_device(driver->of_match_table, &pdev->dev);
+       match = of_match_device(driver->of_match_table, dev);
        hisi_pcie->soc_ops = (struct pcie_soc_ops *) match->data;
 
        hisi_pcie->subctrl =
        syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl");
        if (IS_ERR(hisi_pcie->subctrl)) {
-               dev_err(pp->dev, "cannot get subctrl base\n");
+               dev_err(dev, "cannot get subctrl base\n");
                return PTR_ERR(hisi_pcie->subctrl);
        }
 
        reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi");
-       hisi_pcie->reg_base = devm_ioremap_resource(&pdev->dev, reg);
-       if (IS_ERR(hisi_pcie->reg_base)) {
-               dev_err(pp->dev, "cannot get rc_dbi base\n");
-               return PTR_ERR(hisi_pcie->reg_base);
+       pp->dbi_base = devm_ioremap_resource(dev, reg);
+       if (IS_ERR(pp->dbi_base)) {
+               dev_err(dev, "cannot get rc_dbi base\n");
+               return PTR_ERR(pp->dbi_base);
        }
 
-       hisi_pcie->pp.dbi_base = hisi_pcie->reg_base;
-
-       ret = hisi_add_pcie_port(pp, pdev);
+       ret = hisi_add_pcie_port(hisi_pcie, pdev);
        if (ret)
                return ret;
 
-       platform_set_drvdata(pdev, hisi_pcie);
-
-       dev_warn(pp->dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
+       dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
 
        return 0;
 }
index 0d7bee4a0d26da4221f7a16899384a7e4e44420a..8ce089043a27dcd05be911bd1cd953e4abe92af0 100644 (file)
@@ -42,19 +42,24 @@ static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 
 static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
 {
+       struct device *dev = &bdev->dev;
        struct iproc_pcie *pcie;
        LIST_HEAD(res);
        struct resource res_mem;
        int ret;
 
-       pcie = devm_kzalloc(&bdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
-       pcie->dev = &bdev->dev;
-       bcma_set_drvdata(bdev, pcie);
+       pcie->dev = dev;
 
        pcie->base = bdev->io_addr;
+       if (!pcie->base) {
+               dev_err(dev, "no controller registers\n");
+               return -ENOMEM;
+       }
+
        pcie->base_addr = bdev->addr;
 
        res_mem.start = bdev->addr_s[0];
@@ -67,10 +72,11 @@ static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
 
        ret = iproc_pcie_setup(pcie, &res);
        if (ret)
-               dev_err(pcie->dev, "PCIe controller setup failed\n");
+               dev_err(dev, "PCIe controller setup failed\n");
 
        pci_free_resource_list(&res);
 
+       bcma_set_drvdata(bdev, pcie);
        return ret;
 }
 
index 1738c5288eb6b06bc7806e725cdfd999a9609f8a..a3de087976b31b2586d0f1f064ee892805cac4b9 100644 (file)
@@ -40,35 +40,35 @@ MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
 
 static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        const struct of_device_id *of_id;
        struct iproc_pcie *pcie;
-       struct device_node *np = pdev->dev.of_node;
+       struct device_node *np = dev->of_node;
        struct resource reg;
        resource_size_t iobase = 0;
        LIST_HEAD(res);
        int ret;
 
-       of_id = of_match_device(iproc_pcie_of_match_table, &pdev->dev);
+       of_id = of_match_device(iproc_pcie_of_match_table, dev);
        if (!of_id)
                return -EINVAL;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
-       pcie->dev = &pdev->dev;
+       pcie->dev = dev;
        pcie->type = (enum iproc_pcie_type)of_id->data;
-       platform_set_drvdata(pdev, pcie);
 
        ret = of_address_to_resource(np, 0, &reg);
        if (ret < 0) {
-               dev_err(pcie->dev, "unable to obtain controller resources\n");
+               dev_err(dev, "unable to obtain controller resources\n");
                return ret;
        }
 
-       pcie->base = devm_ioremap(pcie->dev, reg.start, resource_size(&reg));
+       pcie->base = devm_ioremap(dev, reg.start, resource_size(&reg));
        if (!pcie->base) {
-               dev_err(pcie->dev, "unable to map controller registers\n");
+               dev_err(dev, "unable to map controller registers\n");
                return -ENOMEM;
        }
        pcie->base_addr = reg.start;
@@ -79,7 +79,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
                ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset",
                                           &val);
                if (ret) {
-                       dev_err(pcie->dev,
+                       dev_err(dev,
                                "missing brcm,pcie-ob-axi-offset property\n");
                        return ret;
                }
@@ -88,7 +88,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
                ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
                                           &val);
                if (ret) {
-                       dev_err(pcie->dev,
+                       dev_err(dev,
                                "missing brcm,pcie-ob-window-size property\n");
                        return ret;
                }
@@ -101,7 +101,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
        }
 
        /* PHY use is optional */
-       pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy");
+       pcie->phy = devm_phy_get(dev, "pcie-phy");
        if (IS_ERR(pcie->phy)) {
                if (PTR_ERR(pcie->phy) == -EPROBE_DEFER)
                        return -EPROBE_DEFER;
@@ -110,7 +110,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
 
        ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &iobase);
        if (ret) {
-               dev_err(pcie->dev,
+               dev_err(dev,
                        "unable to get PCI host bridge resources\n");
                return ret;
        }
@@ -119,10 +119,11 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
 
        ret = iproc_pcie_setup(pcie, &res);
        if (ret)
-               dev_err(pcie->dev, "PCIe controller setup failed\n");
+               dev_err(dev, "PCIe controller setup failed\n");
 
        pci_free_resource_list(&res);
 
+       platform_set_drvdata(pdev, pcie);
        return ret;
 }
 
index e167b2f0098d687c6a197b49e2b86e56dded9514..0b999a9fb843e8444aefe0e15c733145924fc786 100644 (file)
@@ -63,6 +63,8 @@
 #define OARR_SIZE_CFG_SHIFT          1
 #define OARR_SIZE_CFG                BIT(OARR_SIZE_CFG_SHIFT)
 
+#define PCI_EXP_CAP                    0xac
+
 #define MAX_NUM_OB_WINDOWS           2
 
 #define IPROC_PCIE_REG_INVALID 0xffff
@@ -258,9 +260,10 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
 
 static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
 {
+       struct device *dev = pcie->dev;
        u8 hdr_type;
        u32 link_ctrl, class, val;
-       u16 pos, link_status;
+       u16 pos = PCI_EXP_CAP, link_status;
        bool link_is_active = false;
 
        /*
@@ -272,14 +275,14 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
 
        val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS);
        if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) {
-               dev_err(pcie->dev, "PHY or data link is INACTIVE!\n");
+               dev_err(dev, "PHY or data link is INACTIVE!\n");
                return -ENODEV;
        }
 
        /* make sure we are not in EP mode */
        pci_bus_read_config_byte(bus, 0, PCI_HEADER_TYPE, &hdr_type);
        if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) {
-               dev_err(pcie->dev, "in EP mode, hdr=%#02x\n", hdr_type);
+               dev_err(dev, "in EP mode, hdr=%#02x\n", hdr_type);
                return -EFAULT;
        }
 
@@ -293,30 +296,27 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
        pci_bus_write_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, class);
 
        /* check link status to see if link is active */
-       pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP);
        pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status);
        if (link_status & PCI_EXP_LNKSTA_NLW)
                link_is_active = true;
 
        if (!link_is_active) {
                /* try GEN 1 link speed */
-#define PCI_LINK_STATUS_CTRL_2_OFFSET 0x0dc
 #define PCI_TARGET_LINK_SPEED_MASK    0xf
 #define PCI_TARGET_LINK_SPEED_GEN2    0x2
 #define PCI_TARGET_LINK_SPEED_GEN1    0x1
                pci_bus_read_config_dword(bus, 0,
-                                         PCI_LINK_STATUS_CTRL_2_OFFSET,
+                                         pos + PCI_EXP_LNKCTL2,
                                          &link_ctrl);
                if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) ==
                    PCI_TARGET_LINK_SPEED_GEN2) {
                        link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK;
                        link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1;
                        pci_bus_write_config_dword(bus, 0,
-                                          PCI_LINK_STATUS_CTRL_2_OFFSET,
+                                          pos + PCI_EXP_LNKCTL2,
                                           link_ctrl);
                        msleep(100);
 
-                       pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP);
                        pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA,
                                                 &link_status);
                        if (link_status & PCI_EXP_LNKSTA_NLW)
@@ -324,7 +324,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
                }
        }
 
-       dev_info(pcie->dev, "link: %s\n", link_is_active ? "UP" : "DOWN");
+       dev_info(dev, "link: %s\n", link_is_active ? "UP" : "DOWN");
 
        return link_is_active ? 0 : -ENODEV;
 }
@@ -349,12 +349,13 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
                               u64 pci_addr, resource_size_t size)
 {
        struct iproc_pcie_ob *ob = &pcie->ob;
+       struct device *dev = pcie->dev;
        unsigned i;
        u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
        u64 remainder;
 
        if (size > max_size) {
-               dev_err(pcie->dev,
+               dev_err(dev,
                        "res size %pap exceeds max supported size 0x%llx\n",
                        &size, max_size);
                return -EINVAL;
@@ -362,15 +363,14 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
 
        div64_u64_rem(size, ob->window_size, &remainder);
        if (remainder) {
-               dev_err(pcie->dev,
+               dev_err(dev,
                        "res size %pap needs to be multiple of window size %pap\n",
                        &size, &ob->window_size);
                return -EINVAL;
        }
 
        if (axi_addr < ob->axi_offset) {
-               dev_err(pcie->dev,
-                       "axi address %pap less than offset %pap\n",
+               dev_err(dev, "axi address %pap less than offset %pap\n",
                        &axi_addr, &ob->axi_offset);
                return -EINVAL;
        }
@@ -406,6 +406,7 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
 static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
                                 struct list_head *resources)
 {
+       struct device *dev = pcie->dev;
        struct resource_entry *window;
        int ret;
 
@@ -425,7 +426,7 @@ static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
                                return ret;
                        break;
                default:
-                       dev_err(pcie->dev, "invalid resource %pR\n", res);
+                       dev_err(dev, "invalid resource %pR\n", res);
                        return -EINVAL;
                }
        }
@@ -455,26 +456,25 @@ static void iproc_pcie_msi_disable(struct iproc_pcie *pcie)
 
 int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 {
+       struct device *dev;
        int ret;
        void *sysdata;
        struct pci_bus *bus;
 
-       if (!pcie || !pcie->dev || !pcie->base)
-               return -EINVAL;
-
-       ret = devm_request_pci_bus_resources(pcie->dev, res);
+       dev = pcie->dev;
+       ret = devm_request_pci_bus_resources(dev, res);
        if (ret)
                return ret;
 
        ret = phy_init(pcie->phy);
        if (ret) {
-               dev_err(pcie->dev, "unable to initialize PCIe PHY\n");
+               dev_err(dev, "unable to initialize PCIe PHY\n");
                return ret;
        }
 
        ret = phy_power_on(pcie->phy);
        if (ret) {
-               dev_err(pcie->dev, "unable to power on PCIe PHY\n");
+               dev_err(dev, "unable to power on PCIe PHY\n");
                goto err_exit_phy;
        }
 
@@ -486,7 +486,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
                pcie->reg_offsets = iproc_pcie_reg_paxc;
                break;
        default:
-               dev_err(pcie->dev, "incompatible iProc PCIe interface\n");
+               dev_err(dev, "incompatible iProc PCIe interface\n");
                ret = -EINVAL;
                goto err_power_off_phy;
        }
@@ -496,7 +496,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
        if (pcie->need_ob_cfg) {
                ret = iproc_pcie_map_ranges(pcie, res);
                if (ret) {
-                       dev_err(pcie->dev, "map failed\n");
+                       dev_err(dev, "map failed\n");
                        goto err_power_off_phy;
                }
        }
@@ -508,9 +508,9 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
        sysdata = pcie;
 #endif
 
-       bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops, sysdata, res);
+       bus = pci_create_root_bus(dev, 0, &iproc_pcie_ops, sysdata, res);
        if (!bus) {
-               dev_err(pcie->dev, "unable to create PCI root bus\n");
+               dev_err(dev, "unable to create PCI root bus\n");
                ret = -ENOMEM;
                goto err_power_off_phy;
        }
@@ -518,7 +518,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 
        ret = iproc_pcie_check_link(pcie, bus);
        if (ret) {
-               dev_err(pcie->dev, "no PCIe EP device detected\n");
+               dev_err(dev, "no PCIe EP device detected\n");
                goto err_rm_root_bus;
        }
 
@@ -526,7 +526,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
                if (iproc_pcie_msi_enable(pcie))
-                       dev_info(pcie->dev, "not using iProc MSI\n");
+                       dev_info(dev, "not using iProc MSI\n");
 
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
index 5ec2d440a6b724b741a08db4d142f80b131c182f..ef0a84c7a5885e1d62df499353f1384d86a5fceb 100644 (file)
@@ -86,12 +86,10 @@ struct qcom_pcie_ops {
 };
 
 struct qcom_pcie {
-       struct pcie_port pp;
-       struct device *dev;
+       struct pcie_port pp;                    /* pp.dbi_base is DT dbi */
+       void __iomem *parf;                     /* DT parf */
+       void __iomem *elbi;                     /* DT elbi */
        union qcom_pcie_resources res;
-       void __iomem *parf;
-       void __iomem *dbi;
-       void __iomem *elbi;
        struct phy *phy;
        struct gpio_desc *reset;
        struct qcom_pcie_ops *ops;
@@ -136,7 +134,7 @@ static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
 static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
 {
        struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
-       struct device *dev = pcie->dev;
+       struct device *dev = pcie->pp.dev;
 
        res->vdda = devm_regulator_get(dev, "vdda");
        if (IS_ERR(res->vdda))
@@ -188,7 +186,7 @@ static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
 static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie)
 {
        struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
-       struct device *dev = pcie->dev;
+       struct device *dev = pcie->pp.dev;
 
        res->vdda = devm_regulator_get(dev, "vdda");
        if (IS_ERR(res->vdda))
@@ -237,7 +235,7 @@ static void qcom_pcie_deinit_v0(struct qcom_pcie *pcie)
 static int qcom_pcie_init_v0(struct qcom_pcie *pcie)
 {
        struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
-       struct device *dev = pcie->dev;
+       struct device *dev = pcie->pp.dev;
        u32 val;
        int ret;
 
@@ -359,7 +357,7 @@ static void qcom_pcie_deinit_v1(struct qcom_pcie *pcie)
 static int qcom_pcie_init_v1(struct qcom_pcie *pcie)
 {
        struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
-       struct device *dev = pcie->dev;
+       struct device *dev = pcie->pp.dev;
        int ret;
 
        ret = reset_control_deassert(res->core);
@@ -426,7 +424,7 @@ err_res:
 static int qcom_pcie_link_up(struct pcie_port *pp)
 {
        struct qcom_pcie *pcie = to_qcom_pcie(pp);
-       u16 val = readw(pcie->dbi + PCIE20_CAP + PCI_EXP_LNKSTA);
+       u16 val = readw(pcie->pp.dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA);
 
        return !!(val & PCI_EXP_LNKSTA_DLLLA);
 }
@@ -509,8 +507,8 @@ static int qcom_pcie_probe(struct platform_device *pdev)
        if (!pcie)
                return -ENOMEM;
 
+       pp = &pcie->pp;
        pcie->ops = (struct qcom_pcie_ops *)of_device_get_match_data(dev);
-       pcie->dev = dev;
 
        pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW);
        if (IS_ERR(pcie->reset))
@@ -522,9 +520,9 @@ static int qcom_pcie_probe(struct platform_device *pdev)
                return PTR_ERR(pcie->parf);
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
-       pcie->dbi = devm_ioremap_resource(dev, res);
-       if (IS_ERR(pcie->dbi))
-               return PTR_ERR(pcie->dbi);
+       pp->dbi_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pp->dbi_base))
+               return PTR_ERR(pp->dbi_base);
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
        pcie->elbi = devm_ioremap_resource(dev, res);
@@ -539,9 +537,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       pp = &pcie->pp;
        pp->dev = dev;
-       pp->dbi_base = pcie->dbi;
        pp->root_bus_nr = -1;
        pp->ops = &qcom_pcie_dw_ops;
 
@@ -569,8 +565,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
                return ret;
        }
 
-       platform_set_drvdata(pdev, pcie);
-
        return 0;
 }
 
index e06b1d3b4dea60e145d563ce87d463d1831e1b93..62700d1896f4b885517cab4533b5e4c2c1a6db47 100644 (file)
@@ -31,8 +31,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
-#define DRV_NAME "rcar-pcie"
-
 #define PCIECAR                        0x000010
 #define PCIECCTLR              0x000018
 #define  CONFIG_SEND_ENABLE    (1 << 31)
@@ -397,6 +395,7 @@ static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pci)
 
 static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        unsigned int timeout = 1000;
        u32 macsr;
 
@@ -404,7 +403,7 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
                return;
 
        if (rcar_pci_read_reg(pcie, MACCTLR) & SPEED_CHANGE) {
-               dev_err(pcie->dev, "Speed change already in progress\n");
+               dev_err(dev, "Speed change already in progress\n");
                return;
        }
 
@@ -433,7 +432,7 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
                        rcar_pci_write_reg(pcie, macsr, MACSR);
 
                        if (macsr & SPCHGFAIL)
-                               dev_err(pcie->dev, "Speed change failed\n");
+                               dev_err(dev, "Speed change failed\n");
 
                        goto done;
                }
@@ -441,15 +440,16 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
                msleep(1);
        };
 
-       dev_err(pcie->dev, "Speed change timed out\n");
+       dev_err(dev, "Speed change timed out\n");
 
 done:
-       dev_info(pcie->dev, "Current link speed is %s GT/s\n",
+       dev_info(dev, "Current link speed is %s GT/s\n",
                 (macsr & LINK_SPEED) == LINK_SPEED_5_0GTS ? "5" : "2.5");
 }
 
 static int rcar_pcie_enable(struct rcar_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        struct pci_bus *bus, *child;
        LIST_HEAD(res);
 
@@ -461,14 +461,14 @@ static int rcar_pcie_enable(struct rcar_pcie *pcie)
        pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
-               bus = pci_scan_root_bus_msi(pcie->dev, pcie->root_bus_nr,
+               bus = pci_scan_root_bus_msi(dev, pcie->root_bus_nr,
                                &rcar_pcie_ops, pcie, &res, &pcie->msi.chip);
        else
-               bus = pci_scan_root_bus(pcie->dev, pcie->root_bus_nr,
+               bus = pci_scan_root_bus(dev, pcie->root_bus_nr,
                                &rcar_pcie_ops, pcie, &res);
 
        if (!bus) {
-               dev_err(pcie->dev, "Scanning rootbus failed");
+               dev_err(dev, "Scanning rootbus failed");
                return -ENODEV;
        }
 
@@ -487,6 +487,7 @@ static int rcar_pcie_enable(struct rcar_pcie *pcie)
 
 static int phy_wait_for_ack(struct rcar_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        unsigned int timeout = 100;
 
        while (timeout--) {
@@ -496,7 +497,7 @@ static int phy_wait_for_ack(struct rcar_pcie *pcie)
                udelay(100);
        }
 
-       dev_err(pcie->dev, "Access to PCIe phy timed out\n");
+       dev_err(dev, "Access to PCIe phy timed out\n");
 
        return -ETIMEDOUT;
 }
@@ -697,6 +698,7 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)
 {
        struct rcar_pcie *pcie = data;
        struct rcar_msi *msi = &pcie->msi;
+       struct device *dev = pcie->dev;
        unsigned long reg;
 
        reg = rcar_pci_read_reg(pcie, PCIEMSIFR);
@@ -717,10 +719,10 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)
                        if (test_bit(index, msi->used))
                                generic_handle_irq(irq);
                        else
-                               dev_info(pcie->dev, "unhandled MSI\n");
+                               dev_info(dev, "unhandled MSI\n");
                } else {
                        /* Unknown MSI, just clear it */
-                       dev_dbg(pcie->dev, "unexpected MSI\n");
+                       dev_dbg(dev, "unexpected MSI\n");
                }
 
                /* see if there's any more pending in this vector */
@@ -843,22 +845,22 @@ static const struct irq_domain_ops msi_domain_ops = {
 
 static int rcar_pcie_enable_msi(struct rcar_pcie *pcie)
 {
-       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct device *dev = pcie->dev;
        struct rcar_msi *msi = &pcie->msi;
        unsigned long base;
        int err, i;
 
        mutex_init(&msi->lock);
 
-       msi->chip.dev = pcie->dev;
+       msi->chip.dev = dev;
        msi->chip.setup_irq = rcar_msi_setup_irq;
        msi->chip.setup_irqs = rcar_msi_setup_irqs;
        msi->chip.teardown_irq = rcar_msi_teardown_irq;
 
-       msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR,
+       msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR,
                                            &msi_domain_ops, &msi->chip);
        if (!msi->domain) {
-               dev_err(&pdev->dev, "failed to create IRQ domain\n");
+               dev_err(dev, "failed to create IRQ domain\n");
                return -ENOMEM;
        }
 
@@ -866,19 +868,19 @@ static int rcar_pcie_enable_msi(struct rcar_pcie *pcie)
                irq_create_mapping(msi->domain, i);
 
        /* Two irqs are for MSI, but they are also used for non-MSI irqs */
-       err = devm_request_irq(&pdev->dev, msi->irq1, rcar_pcie_msi_irq,
+       err = devm_request_irq(dev, msi->irq1, rcar_pcie_msi_irq,
                               IRQF_SHARED | IRQF_NO_THREAD,
                               rcar_msi_irq_chip.name, pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+               dev_err(dev, "failed to request IRQ: %d\n", err);
                goto err;
        }
 
-       err = devm_request_irq(&pdev->dev, msi->irq2, rcar_pcie_msi_irq,
+       err = devm_request_irq(dev, msi->irq2, rcar_pcie_msi_irq,
                               IRQF_SHARED | IRQF_NO_THREAD,
                               rcar_msi_irq_chip.name, pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+               dev_err(dev, "failed to request IRQ: %d\n", err);
                goto err;
        }
 
@@ -899,32 +901,32 @@ err:
        return err;
 }
 
-static int rcar_pcie_get_resources(struct platform_device *pdev,
-                                  struct rcar_pcie *pcie)
+static int rcar_pcie_get_resources(struct rcar_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        struct resource res;
        int err, i;
 
-       err = of_address_to_resource(pdev->dev.of_node, 0, &res);
+       err = of_address_to_resource(dev->of_node, 0, &res);
        if (err)
                return err;
 
-       pcie->base = devm_ioremap_resource(&pdev->dev, &res);
+       pcie->base = devm_ioremap_resource(dev, &res);
        if (IS_ERR(pcie->base))
                return PTR_ERR(pcie->base);
 
-       pcie->clk = devm_clk_get(&pdev->dev, "pcie");
+       pcie->clk = devm_clk_get(dev, "pcie");
        if (IS_ERR(pcie->clk)) {
-               dev_err(pcie->dev, "cannot get platform clock\n");
+               dev_err(dev, "cannot get platform clock\n");
                return PTR_ERR(pcie->clk);
        }
        err = clk_prepare_enable(pcie->clk);
        if (err)
                return err;
 
-       pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
+       pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
        if (IS_ERR(pcie->bus_clk)) {
-               dev_err(pcie->dev, "cannot get pcie bus clock\n");
+               dev_err(dev, "cannot get pcie bus clock\n");
                err = PTR_ERR(pcie->bus_clk);
                goto fail_clk;
        }
@@ -932,17 +934,17 @@ static int rcar_pcie_get_resources(struct platform_device *pdev,
        if (err)
                goto fail_clk;
 
-       i = irq_of_parse_and_map(pdev->dev.of_node, 0);
+       i = irq_of_parse_and_map(dev->of_node, 0);
        if (!i) {
-               dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n");
+               dev_err(dev, "cannot get platform resources for msi interrupt\n");
                err = -ENOENT;
                goto err_map_reg;
        }
        pcie->msi.irq1 = i;
 
-       i = irq_of_parse_and_map(pdev->dev.of_node, 1);
+       i = irq_of_parse_and_map(dev->of_node, 1);
        if (!i) {
-               dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n");
+               dev_err(dev, "cannot get platform resources for msi interrupt\n");
                err = -ENOENT;
                goto err_map_reg;
        }
@@ -1119,60 +1121,60 @@ out_release_res:
 
 static int rcar_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct rcar_pcie *pcie;
        unsigned int data;
        const struct of_device_id *of_id;
        int err;
        int (*hw_init_fn)(struct rcar_pcie *);
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
-       pcie->dev = &pdev->dev;
-       platform_set_drvdata(pdev, pcie);
+       pcie->dev = dev;
 
        INIT_LIST_HEAD(&pcie->resources);
 
        rcar_pcie_parse_request_of_pci_ranges(pcie);
 
-       err = rcar_pcie_get_resources(pdev, pcie);
+       err = rcar_pcie_get_resources(pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to request resources: %d\n", err);
+               dev_err(dev, "failed to request resources: %d\n", err);
                return err;
        }
 
-       err = rcar_pcie_parse_map_dma_ranges(pcie, pdev->dev.of_node);
+       err = rcar_pcie_parse_map_dma_ranges(pcie, dev->of_node);
        if (err)
                return err;
 
-       of_id = of_match_device(rcar_pcie_of_match, pcie->dev);
+       of_id = of_match_device(rcar_pcie_of_match, dev);
        if (!of_id || !of_id->data)
                return -EINVAL;
        hw_init_fn = of_id->data;
 
-       pm_runtime_enable(pcie->dev);
-       err = pm_runtime_get_sync(pcie->dev);
+       pm_runtime_enable(dev);
+       err = pm_runtime_get_sync(dev);
        if (err < 0) {
-               dev_err(pcie->dev, "pm_runtime_get_sync failed\n");
+               dev_err(dev, "pm_runtime_get_sync failed\n");
                goto err_pm_disable;
        }
 
        /* Failure to get a link might just be that no cards are inserted */
        err = hw_init_fn(pcie);
        if (err) {
-               dev_info(&pdev->dev, "PCIe link down\n");
+               dev_info(dev, "PCIe link down\n");
                err = 0;
                goto err_pm_put;
        }
 
        data = rcar_pci_read_reg(pcie, MACSR);
-       dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
+       dev_info(dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                err = rcar_pcie_enable_msi(pcie);
                if (err < 0) {
-                       dev_err(&pdev->dev,
+                       dev_err(dev,
                                "failed to enable MSI support: %d\n",
                                err);
                        goto err_pm_put;
@@ -1186,16 +1188,16 @@ static int rcar_pcie_probe(struct platform_device *pdev)
        return 0;
 
 err_pm_put:
-       pm_runtime_put(pcie->dev);
+       pm_runtime_put(dev);
 
 err_pm_disable:
-       pm_runtime_disable(pcie->dev);
+       pm_runtime_disable(dev);
        return err;
 }
 
 static struct platform_driver rcar_pcie_driver = {
        .driver = {
-               .name = DRV_NAME,
+               .name = "rcar-pcie",
                .of_match_table = rcar_pcie_of_match,
                .suppress_bind_attrs = true,
        },
index b8c82fc812dc7b604efffe71f8038c0ece24485f..e0b22dab9b7ac37c81d380bfe00751f4496f4516 100644 (file)
@@ -972,7 +972,7 @@ static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie *rockchip,
                return -EINVAL;
        if (region_no == 0) {
                if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits))
-               return -EINVAL;
+                       return -EINVAL;
        }
        if (region_no != 0) {
                if (AXI_REGION_SIZE < (2ULL << num_pass_bits))
@@ -1091,8 +1091,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
        if (err)
                goto err_vpcie;
 
-       platform_set_drvdata(pdev, rockchip);
-
        rockchip_pcie_enable_interrupts(rockchip);
 
        err = rockchip_pcie_init_irq_domain(rockchip);
index 09aed85f275a4b2663aae8db19ae1fc1b8f7cef4..3cf197ba7f37baa6dd9613844cd21767e3caef93 100644 (file)
 #include "pcie-designware.h"
 
 struct spear13xx_pcie {
+       struct pcie_port        pp;             /* DT dbi is pp.dbi_base */
        void __iomem            *app_base;
        struct phy              *phy;
        struct clk              *clk;
-       struct pcie_port        pp;
        bool                    is_gen1;
 };
 
@@ -57,96 +57,26 @@ struct pcie_app_reg {
 };
 
 /* CR0 ID */
-#define RX_LANE_FLIP_EN_ID                     0
-#define TX_LANE_FLIP_EN_ID                     1
-#define SYS_AUX_PWR_DET_ID                     2
 #define APP_LTSSM_ENABLE_ID                    3
-#define SYS_ATTEN_BUTTON_PRESSED_ID            4
-#define SYS_MRL_SENSOR_STATE_ID                        5
-#define SYS_PWR_FAULT_DET_ID                   6
-#define SYS_MRL_SENSOR_CHGED_ID                        7
-#define SYS_PRE_DET_CHGED_ID                   8
-#define SYS_CMD_CPLED_INT_ID                   9
-#define APP_INIT_RST_0_ID                      11
-#define APP_REQ_ENTR_L1_ID                     12
-#define APP_READY_ENTR_L23_ID                  13
-#define APP_REQ_EXIT_L1_ID                     14
-#define DEVICE_TYPE_EP                         (0 << 25)
-#define DEVICE_TYPE_LEP                                (1 << 25)
 #define DEVICE_TYPE_RC                         (4 << 25)
-#define SYS_INT_ID                             29
 #define MISCTRL_EN_ID                          30
 #define REG_TRANSLATION_ENABLE                 31
 
-/* CR1 ID */
-#define APPS_PM_XMT_TURNOFF_ID                 2
-#define APPS_PM_XMT_PME_ID                     5
-
 /* CR3 ID */
-#define XMLH_LTSSM_STATE_DETECT_QUIET          0x00
-#define XMLH_LTSSM_STATE_DETECT_ACT            0x01
-#define XMLH_LTSSM_STATE_POLL_ACTIVE           0x02
-#define XMLH_LTSSM_STATE_POLL_COMPLIANCE       0x03
-#define XMLH_LTSSM_STATE_POLL_CONFIG           0x04
-#define XMLH_LTSSM_STATE_PRE_DETECT_QUIET      0x05
-#define XMLH_LTSSM_STATE_DETECT_WAIT           0x06
-#define XMLH_LTSSM_STATE_CFG_LINKWD_START      0x07
-#define XMLH_LTSSM_STATE_CFG_LINKWD_ACEPT      0x08
-#define XMLH_LTSSM_STATE_CFG_LANENUM_WAIT      0x09
-#define XMLH_LTSSM_STATE_CFG_LANENUM_ACEPT     0x0A
-#define XMLH_LTSSM_STATE_CFG_COMPLETE          0x0B
-#define XMLH_LTSSM_STATE_CFG_IDLE              0x0C
-#define XMLH_LTSSM_STATE_RCVRY_LOCK            0x0D
-#define XMLH_LTSSM_STATE_RCVRY_SPEED           0x0E
-#define XMLH_LTSSM_STATE_RCVRY_RCVRCFG         0x0F
-#define XMLH_LTSSM_STATE_RCVRY_IDLE            0x10
-#define XMLH_LTSSM_STATE_L0                    0x11
-#define XMLH_LTSSM_STATE_L0S                   0x12
-#define XMLH_LTSSM_STATE_L123_SEND_EIDLE       0x13
-#define XMLH_LTSSM_STATE_L1_IDLE               0x14
-#define XMLH_LTSSM_STATE_L2_IDLE               0x15
-#define XMLH_LTSSM_STATE_L2_WAKE               0x16
-#define XMLH_LTSSM_STATE_DISABLED_ENTRY                0x17
-#define XMLH_LTSSM_STATE_DISABLED_IDLE         0x18
-#define XMLH_LTSSM_STATE_DISABLED              0x19
-#define XMLH_LTSSM_STATE_LPBK_ENTRY            0x1A
-#define XMLH_LTSSM_STATE_LPBK_ACTIVE           0x1B
-#define XMLH_LTSSM_STATE_LPBK_EXIT             0x1C
-#define XMLH_LTSSM_STATE_LPBK_EXIT_TIMEOUT     0x1D
-#define XMLH_LTSSM_STATE_HOT_RESET_ENTRY       0x1E
-#define XMLH_LTSSM_STATE_HOT_RESET             0x1F
-#define XMLH_LTSSM_STATE_MASK                  0x3F
 #define XMLH_LINK_UP                           (1 << 6)
 
-/* CR4 ID */
-#define CFG_MSI_EN_ID                          18
-
 /* CR6 */
-#define INTA_CTRL_INT                          (1 << 7)
-#define INTB_CTRL_INT                          (1 << 8)
-#define INTC_CTRL_INT                          (1 << 9)
-#define INTD_CTRL_INT                          (1 << 10)
 #define MSI_CTRL_INT                           (1 << 26)
 
-/* CR19 ID */
-#define VEN_MSI_REQ_ID                         11
-#define VEN_MSI_FUN_NUM_ID                     8
-#define VEN_MSI_TC_ID                          5
-#define VEN_MSI_VECTOR_ID                      0
-#define VEN_MSI_REQ_EN         ((u32)0x1 << VEN_MSI_REQ_ID)
-#define VEN_MSI_FUN_NUM_MASK   ((u32)0x7 << VEN_MSI_FUN_NUM_ID)
-#define VEN_MSI_TC_MASK                ((u32)0x7 << VEN_MSI_TC_ID)
-#define VEN_MSI_VECTOR_MASK    ((u32)0x1F << VEN_MSI_VECTOR_ID)
-
 #define EXP_CAP_ID_OFFSET                      0x70
 
 #define to_spear13xx_pcie(x)   container_of(x, struct spear13xx_pcie, pp)
 
-static int spear13xx_pcie_establish_link(struct pcie_port *pp)
+static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie)
 {
-       u32 val;
-       struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
+       struct pcie_port *pp = &spear13xx_pcie->pp;
        struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
+       u32 val;
        u32 exp_cap_off = EXP_CAP_ID_OFFSET;
 
        if (dw_pcie_link_up(pp)) {
@@ -203,9 +133,9 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
 
 static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
-       struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
+       struct spear13xx_pcie *spear13xx_pcie = arg;
        struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
+       struct pcie_port *pp = &spear13xx_pcie->pp;
        unsigned int status;
 
        status = readl(&app_reg->int_sts);
@@ -220,9 +150,9 @@ static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg)
        return IRQ_HANDLED;
 }
 
-static void spear13xx_pcie_enable_interrupts(struct pcie_port *pp)
+static void spear13xx_pcie_enable_interrupts(struct spear13xx_pcie *spear13xx_pcie)
 {
-       struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
+       struct pcie_port *pp = &spear13xx_pcie->pp;
        struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
 
        /* Enable MSI interrupt */
@@ -246,8 +176,10 @@ static int spear13xx_pcie_link_up(struct pcie_port *pp)
 
 static void spear13xx_pcie_host_init(struct pcie_port *pp)
 {
-       spear13xx_pcie_establish_link(pp);
-       spear13xx_pcie_enable_interrupts(pp);
+       struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
+
+       spear13xx_pcie_establish_link(spear13xx_pcie);
+       spear13xx_pcie_enable_interrupts(spear13xx_pcie);
 }
 
 static struct pcie_host_ops spear13xx_pcie_host_ops = {
@@ -255,10 +187,11 @@ static struct pcie_host_ops spear13xx_pcie_host_ops = {
        .host_init = spear13xx_pcie_host_init,
 };
 
-static int spear13xx_add_pcie_port(struct pcie_port *pp,
-                                        struct platform_device *pdev)
+static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie,
+                                  struct platform_device *pdev)
 {
-       struct device *dev = &pdev->dev;
+       struct pcie_port *pp = &spear13xx_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        pp->irq = platform_get_irq(pdev, 0);
@@ -268,7 +201,7 @@ static int spear13xx_add_pcie_port(struct pcie_port *pp,
        }
        ret = devm_request_irq(dev, pp->irq, spear13xx_pcie_irq_handler,
                               IRQF_SHARED | IRQF_NO_THREAD,
-                              "spear1340-pcie", pp);
+                              "spear1340-pcie", spear13xx_pcie);
        if (ret) {
                dev_err(dev, "failed to request irq %d\n", pp->irq);
                return ret;
@@ -288,10 +221,10 @@ static int spear13xx_add_pcie_port(struct pcie_port *pp,
 
 static int spear13xx_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct spear13xx_pcie *spear13xx_pcie;
        struct pcie_port *pp;
-       struct device *dev = &pdev->dev;
-       struct device_node *np = pdev->dev.of_node;
+       struct device_node *np = dev->of_node;
        struct resource *dbi_base;
        int ret;
 
@@ -323,7 +256,6 @@ static int spear13xx_pcie_probe(struct platform_device *pdev)
        }
 
        pp = &spear13xx_pcie->pp;
-
        pp->dev = dev;
 
        dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
@@ -338,7 +270,7 @@ static int spear13xx_pcie_probe(struct platform_device *pdev)
        if (of_property_read_bool(np, "st,pcie-is-gen1"))
                spear13xx_pcie->is_gen1 = true;
 
-       ret = spear13xx_add_pcie_port(pp, pdev);
+       ret = spear13xx_add_pcie_port(spear13xx_pcie, pdev);
        if (ret < 0)
                goto fail_clk;
 
index 67eae4179290e308936a953ac6d6a7753d95259a..43eaa4afab9491364d660bb4ea6d07d50810cf4c 100644 (file)
@@ -212,6 +212,7 @@ static bool nwl_phy_link_up(struct nwl_pcie *pcie)
 
 static int nwl_wait_for_link(struct nwl_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        int retries;
 
        /* check if the link is up or not */
@@ -221,7 +222,7 @@ static int nwl_wait_for_link(struct nwl_pcie *pcie)
                usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
        }
 
-       dev_err(pcie->dev, "PHY link never came up\n");
+       dev_err(dev, "PHY link never came up\n");
        return -ETIMEDOUT;
 }
 
@@ -277,6 +278,7 @@ static struct pci_ops nwl_pcie_ops = {
 static irqreturn_t nwl_pcie_misc_handler(int irq, void *data)
 {
        struct nwl_pcie *pcie = data;
+       struct device *dev = pcie->dev;
        u32 misc_stat;
 
        /* Checking for misc interrupts */
@@ -286,45 +288,43 @@ static irqreturn_t nwl_pcie_misc_handler(int irq, void *data)
                return IRQ_NONE;
 
        if (misc_stat & MSGF_MISC_SR_RXMSG_OVER)
-               dev_err(pcie->dev, "Received Message FIFO Overflow\n");
+               dev_err(dev, "Received Message FIFO Overflow\n");
 
        if (misc_stat & MSGF_MISC_SR_SLAVE_ERR)
-               dev_err(pcie->dev, "Slave error\n");
+               dev_err(dev, "Slave error\n");
 
        if (misc_stat & MSGF_MISC_SR_MASTER_ERR)
-               dev_err(pcie->dev, "Master error\n");
+               dev_err(dev, "Master error\n");
 
        if (misc_stat & MSGF_MISC_SR_I_ADDR_ERR)
-               dev_err(pcie->dev,
-                       "In Misc Ingress address translation error\n");
+               dev_err(dev, "In Misc Ingress address translation error\n");
 
        if (misc_stat & MSGF_MISC_SR_E_ADDR_ERR)
-               dev_err(pcie->dev,
-                       "In Misc Egress address translation error\n");
+               dev_err(dev, "In Misc Egress address translation error\n");
 
        if (misc_stat & MSGF_MISC_SR_FATAL_AER)
-               dev_err(pcie->dev, "Fatal Error in AER Capability\n");
+               dev_err(dev, "Fatal Error in AER Capability\n");
 
        if (misc_stat & MSGF_MISC_SR_NON_FATAL_AER)
-               dev_err(pcie->dev, "Non-Fatal Error in AER Capability\n");
+               dev_err(dev, "Non-Fatal Error in AER Capability\n");
 
        if (misc_stat & MSGF_MISC_SR_CORR_AER)
-               dev_err(pcie->dev, "Correctable Error in AER Capability\n");
+               dev_err(dev, "Correctable Error in AER Capability\n");
 
        if (misc_stat & MSGF_MISC_SR_UR_DETECT)
-               dev_err(pcie->dev, "Unsupported request Detected\n");
+               dev_err(dev, "Unsupported request Detected\n");
 
        if (misc_stat & MSGF_MISC_SR_NON_FATAL_DEV)
-               dev_err(pcie->dev, "Non-Fatal Error Detected\n");
+               dev_err(dev, "Non-Fatal Error Detected\n");
 
        if (misc_stat & MSGF_MISC_SR_FATAL_DEV)
-               dev_err(pcie->dev, "Fatal Error Detected\n");
+               dev_err(dev, "Fatal Error Detected\n");
 
        if (misc_stat & MSGF_MSIC_SR_LINK_AUTO_BWIDTH)
-               dev_info(pcie->dev, "Link Autonomous Bandwidth Management Status bit set\n");
+               dev_info(dev, "Link Autonomous Bandwidth Management Status bit set\n");
 
        if (misc_stat & MSGF_MSIC_SR_LINK_BWIDTH)
-               dev_info(pcie->dev, "Link Bandwidth Management Status bit set\n");
+               dev_info(dev, "Link Bandwidth Management Status bit set\n");
 
        /* Clear misc interrupt status */
        nwl_bridge_writel(pcie, misc_stat, MSGF_MISC_STATUS);
@@ -494,20 +494,21 @@ static const struct irq_domain_ops dev_msi_domain_ops = {
 static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie)
 {
 #ifdef CONFIG_PCI_MSI
-       struct fwnode_handle *fwnode = of_node_to_fwnode(pcie->dev->of_node);
+       struct device *dev = pcie->dev;
+       struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
        struct nwl_msi *msi = &pcie->msi;
 
        msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR,
                                                &dev_msi_domain_ops, pcie);
        if (!msi->dev_domain) {
-               dev_err(pcie->dev, "failed to create dev IRQ domain\n");
+               dev_err(dev, "failed to create dev IRQ domain\n");
                return -ENOMEM;
        }
        msi->msi_domain = pci_msi_create_irq_domain(fwnode,
                                                    &nwl_msi_domain_info,
                                                    msi->dev_domain);
        if (!msi->msi_domain) {
-               dev_err(pcie->dev, "failed to create msi IRQ domain\n");
+               dev_err(dev, "failed to create msi IRQ domain\n");
                irq_domain_remove(msi->dev_domain);
                return -ENOMEM;
        }
@@ -517,12 +518,13 @@ static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie)
 
 static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie)
 {
-       struct device_node *node = pcie->dev->of_node;
+       struct device *dev = pcie->dev;
+       struct device_node *node = dev->of_node;
        struct device_node *legacy_intc_node;
 
        legacy_intc_node = of_get_next_child(node, NULL);
        if (!legacy_intc_node) {
-               dev_err(pcie->dev, "No legacy intc node found\n");
+               dev_err(dev, "No legacy intc node found\n");
                return -EINVAL;
        }
 
@@ -532,7 +534,7 @@ static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie)
                                                        pcie);
 
        if (!pcie->legacy_irq_domain) {
-               dev_err(pcie->dev, "failed to create IRQ domain\n");
+               dev_err(dev, "failed to create IRQ domain\n");
                return -ENOMEM;
        }
 
@@ -542,7 +544,8 @@ static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie)
 
 static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus)
 {
-       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct device *dev = pcie->dev;
+       struct platform_device *pdev = to_platform_device(dev);
        struct nwl_msi *msi = &pcie->msi;
        unsigned long base;
        int ret;
@@ -557,7 +560,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus)
        /* Get msi_1 IRQ number */
        msi->irq_msi1 = platform_get_irq_byname(pdev, "msi1");
        if (msi->irq_msi1 < 0) {
-               dev_err(&pdev->dev, "failed to get IRQ#%d\n", msi->irq_msi1);
+               dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi1);
                ret = -EINVAL;
                goto err;
        }
@@ -568,7 +571,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus)
        /* Get msi_0 IRQ number */
        msi->irq_msi0 = platform_get_irq_byname(pdev, "msi0");
        if (msi->irq_msi0 < 0) {
-               dev_err(&pdev->dev, "failed to get IRQ#%d\n", msi->irq_msi0);
+               dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi0);
                ret = -EINVAL;
                goto err;
        }
@@ -579,7 +582,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus)
        /* Check for msii_present bit */
        ret = nwl_bridge_readl(pcie, I_MSII_CAPABILITIES) & MSII_PRESENT;
        if (!ret) {
-               dev_err(pcie->dev, "MSI not present\n");
+               dev_err(dev, "MSI not present\n");
                ret = -EIO;
                goto err;
        }
@@ -628,13 +631,14 @@ err:
 
 static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
 {
-       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct device *dev = pcie->dev;
+       struct platform_device *pdev = to_platform_device(dev);
        u32 breg_val, ecam_val, first_busno = 0;
        int err;
 
        breg_val = nwl_bridge_readl(pcie, E_BREG_CAPABILITIES) & BREG_PRESENT;
        if (!breg_val) {
-               dev_err(pcie->dev, "BREG is not present\n");
+               dev_err(dev, "BREG is not present\n");
                return breg_val;
        }
 
@@ -665,7 +669,7 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
 
        ecam_val = nwl_bridge_readl(pcie, E_ECAM_CAPABILITIES) & E_ECAM_PRESENT;
        if (!ecam_val) {
-               dev_err(pcie->dev, "ECAM is not present\n");
+               dev_err(dev, "ECAM is not present\n");
                return ecam_val;
        }
 
@@ -692,23 +696,23 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
        writel(ecam_val, (pcie->ecam_base + PCI_PRIMARY_BUS));
 
        if (nwl_pcie_link_up(pcie))
-               dev_info(pcie->dev, "Link is UP\n");
+               dev_info(dev, "Link is UP\n");
        else
-               dev_info(pcie->dev, "Link is DOWN\n");
+               dev_info(dev, "Link is DOWN\n");
 
        /* Get misc IRQ number */
        pcie->irq_misc = platform_get_irq_byname(pdev, "misc");
        if (pcie->irq_misc < 0) {
-               dev_err(&pdev->dev, "failed to get misc IRQ %d\n",
+               dev_err(dev, "failed to get misc IRQ %d\n",
                        pcie->irq_misc);
                return -EINVAL;
        }
 
-       err = devm_request_irq(pcie->dev, pcie->irq_misc,
+       err = devm_request_irq(dev, pcie->irq_misc,
                               nwl_pcie_misc_handler, IRQF_SHARED,
                               "nwl_pcie:misc", pcie);
        if (err) {
-               dev_err(pcie->dev, "fail to register misc IRQ#%d\n",
+               dev_err(dev, "fail to register misc IRQ#%d\n",
                        pcie->irq_misc);
                return err;
        }
@@ -744,31 +748,32 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
 static int nwl_pcie_parse_dt(struct nwl_pcie *pcie,
                             struct platform_device *pdev)
 {
-       struct device_node *node = pcie->dev->of_node;
+       struct device *dev = pcie->dev;
+       struct device_node *node = dev->of_node;
        struct resource *res;
        const char *type;
 
        /* Check for device type */
        type = of_get_property(node, "device_type", NULL);
        if (!type || strcmp(type, "pci")) {
-               dev_err(pcie->dev, "invalid \"device_type\" %s\n", type);
+               dev_err(dev, "invalid \"device_type\" %s\n", type);
                return -EINVAL;
        }
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "breg");
-       pcie->breg_base = devm_ioremap_resource(pcie->dev, res);
+       pcie->breg_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(pcie->breg_base))
                return PTR_ERR(pcie->breg_base);
        pcie->phys_breg_base = res->start;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcireg");
-       pcie->pcireg_base = devm_ioremap_resource(pcie->dev, res);
+       pcie->pcireg_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(pcie->pcireg_base))
                return PTR_ERR(pcie->pcireg_base);
        pcie->phys_pcie_reg_base = res->start;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
-       pcie->ecam_base = devm_ioremap_resource(pcie->dev, res);
+       pcie->ecam_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(pcie->ecam_base))
                return PTR_ERR(pcie->ecam_base);
        pcie->phys_ecam_base = res->start;
@@ -776,8 +781,7 @@ static int nwl_pcie_parse_dt(struct nwl_pcie *pcie,
        /* Get intx IRQ number */
        pcie->irq_intx = platform_get_irq_byname(pdev, "intx");
        if (pcie->irq_intx < 0) {
-               dev_err(&pdev->dev, "failed to get intx IRQ %d\n",
-                       pcie->irq_intx);
+               dev_err(dev, "failed to get intx IRQ %d\n", pcie->irq_intx);
                return -EINVAL;
        }
 
@@ -794,7 +798,8 @@ static const struct of_device_id nwl_pcie_of_match[] = {
 
 static int nwl_pcie_probe(struct platform_device *pdev)
 {
-       struct device_node *node = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
        struct nwl_pcie *pcie;
        struct pci_bus *bus;
        struct pci_bus *child;
@@ -802,42 +807,42 @@ static int nwl_pcie_probe(struct platform_device *pdev)
        resource_size_t iobase = 0;
        LIST_HEAD(res);
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
-       pcie->dev = &pdev->dev;
+       pcie->dev = dev;
        pcie->ecam_value = NWL_ECAM_VALUE_DEFAULT;
 
        err = nwl_pcie_parse_dt(pcie, pdev);
        if (err) {
-               dev_err(pcie->dev, "Parsing DT failed\n");
+               dev_err(dev, "Parsing DT failed\n");
                return err;
        }
 
        err = nwl_pcie_bridge_init(pcie);
        if (err) {
-               dev_err(pcie->dev, "HW Initialization failed\n");
+               dev_err(dev, "HW Initialization failed\n");
                return err;
        }
 
        err = of_pci_get_host_bridge_resources(node, 0, 0xff, &res, &iobase);
        if (err) {
-               dev_err(pcie->dev, "Getting bridge resources failed\n");
+               dev_err(dev, "Getting bridge resources failed\n");
                return err;
        }
 
-       err = devm_request_pci_bus_resources(pcie->dev, &res);
+       err = devm_request_pci_bus_resources(dev, &res);
        if (err)
                goto error;
 
        err = nwl_pcie_init_irq_domain(pcie);
        if (err) {
-               dev_err(pcie->dev, "Failed creating IRQ Domain\n");
+               dev_err(dev, "Failed creating IRQ Domain\n");
                goto error;
        }
 
-       bus = pci_create_root_bus(&pdev->dev, pcie->root_busno,
+       bus = pci_create_root_bus(dev, pcie->root_busno,
                                  &nwl_pcie_ops, pcie, &res);
        if (!bus) {
                err = -ENOMEM;
@@ -847,8 +852,7 @@ static int nwl_pcie_probe(struct platform_device *pdev)
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                err = nwl_pcie_enable_msi(pcie, bus);
                if (err < 0) {
-                       dev_err(&pdev->dev,
-                               "failed to enable MSI support: %d\n", err);
+                       dev_err(dev, "failed to enable MSI support: %d\n", err);
                        goto error;
                }
        }
@@ -857,7 +861,6 @@ static int nwl_pcie_probe(struct platform_device *pdev)
        list_for_each_entry(child, &bus->children, node)
                pcie_bus_configure_settings(child);
        pci_bus_add_devices(bus);
-       platform_set_drvdata(pdev, pcie);
        return 0;
 
 error:
index be568039d9d06e86dd33f56675c6fd854e75b588..c8616fadccf166e9302d55134a6c92b6b63750ab 100644 (file)
@@ -140,10 +140,11 @@ static inline bool xilinx_pcie_link_is_up(struct xilinx_pcie_port *port)
  */
 static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port)
 {
+       struct device *dev = port->dev;
        unsigned long val = pcie_read(port, XILINX_PCIE_REG_RPEFR);
 
        if (val & XILINX_PCIE_RPEFR_ERR_VALID) {
-               dev_dbg(port->dev, "Requester ID %lu\n",
+               dev_dbg(dev, "Requester ID %lu\n",
                        val & XILINX_PCIE_RPEFR_REQ_ID);
                pcie_write(port, XILINX_PCIE_RPEFR_ALL_MASK,
                           XILINX_PCIE_REG_RPEFR);
@@ -228,11 +229,10 @@ static void xilinx_pcie_destroy_msi(unsigned int irq)
 
 /**
  * xilinx_pcie_assign_msi - Allocate MSI number
- * @port: PCIe port structure
  *
  * Return: A valid IRQ on success and error value on failure.
  */
-static int xilinx_pcie_assign_msi(struct xilinx_pcie_port *port)
+static int xilinx_pcie_assign_msi(void)
 {
        int pos;
 
@@ -275,7 +275,7 @@ static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip,
        struct msi_msg msg;
        phys_addr_t msg_addr;
 
-       hwirq = xilinx_pcie_assign_msi(port);
+       hwirq = xilinx_pcie_assign_msi();
        if (hwirq < 0)
                return hwirq;
 
@@ -383,6 +383,7 @@ static const struct irq_domain_ops intx_domain_ops = {
 static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
 {
        struct xilinx_pcie_port *port = (struct xilinx_pcie_port *)data;
+       struct device *dev = port->dev;
        u32 val, mask, status, msi_data;
 
        /* Read interrupt decode and mask registers */
@@ -394,32 +395,32 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
                return IRQ_NONE;
 
        if (status & XILINX_PCIE_INTR_LINK_DOWN)
-               dev_warn(port->dev, "Link Down\n");
+               dev_warn(dev, "Link Down\n");
 
        if (status & XILINX_PCIE_INTR_ECRC_ERR)
-               dev_warn(port->dev, "ECRC failed\n");
+               dev_warn(dev, "ECRC failed\n");
 
        if (status & XILINX_PCIE_INTR_STR_ERR)
-               dev_warn(port->dev, "Streaming error\n");
+               dev_warn(dev, "Streaming error\n");
 
        if (status & XILINX_PCIE_INTR_HOT_RESET)
-               dev_info(port->dev, "Hot reset\n");
+               dev_info(dev, "Hot reset\n");
 
        if (status & XILINX_PCIE_INTR_CFG_TIMEOUT)
-               dev_warn(port->dev, "ECAM access timeout\n");
+               dev_warn(dev, "ECAM access timeout\n");
 
        if (status & XILINX_PCIE_INTR_CORRECTABLE) {
-               dev_warn(port->dev, "Correctable error message\n");
+               dev_warn(dev, "Correctable error message\n");
                xilinx_pcie_clear_err_interrupts(port);
        }
 
        if (status & XILINX_PCIE_INTR_NONFATAL) {
-               dev_warn(port->dev, "Non fatal error message\n");
+               dev_warn(dev, "Non fatal error message\n");
                xilinx_pcie_clear_err_interrupts(port);
        }
 
        if (status & XILINX_PCIE_INTR_FATAL) {
-               dev_warn(port->dev, "Fatal error message\n");
+               dev_warn(dev, "Fatal error message\n");
                xilinx_pcie_clear_err_interrupts(port);
        }
 
@@ -429,7 +430,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
 
                /* Check whether interrupt valid */
                if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
-                       dev_warn(port->dev, "RP Intr FIFO1 read error\n");
+                       dev_warn(dev, "RP Intr FIFO1 read error\n");
                        goto error;
                }
 
@@ -451,7 +452,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
                val = pcie_read(port, XILINX_PCIE_REG_RPIFR1);
 
                if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
-                       dev_warn(port->dev, "RP Intr FIFO1 read error\n");
+                       dev_warn(dev, "RP Intr FIFO1 read error\n");
                        goto error;
                }
 
@@ -471,31 +472,31 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
        }
 
        if (status & XILINX_PCIE_INTR_SLV_UNSUPP)
-               dev_warn(port->dev, "Slave unsupported request\n");
+               dev_warn(dev, "Slave unsupported request\n");
 
        if (status & XILINX_PCIE_INTR_SLV_UNEXP)
-               dev_warn(port->dev, "Slave unexpected completion\n");
+               dev_warn(dev, "Slave unexpected completion\n");
 
        if (status & XILINX_PCIE_INTR_SLV_COMPL)
-               dev_warn(port->dev, "Slave completion timeout\n");
+               dev_warn(dev, "Slave completion timeout\n");
 
        if (status & XILINX_PCIE_INTR_SLV_ERRP)
-               dev_warn(port->dev, "Slave Error Poison\n");
+               dev_warn(dev, "Slave Error Poison\n");
 
        if (status & XILINX_PCIE_INTR_SLV_CMPABT)
-               dev_warn(port->dev, "Slave Completer Abort\n");
+               dev_warn(dev, "Slave Completer Abort\n");
 
        if (status & XILINX_PCIE_INTR_SLV_ILLBUR)
-               dev_warn(port->dev, "Slave Illegal Burst\n");
+               dev_warn(dev, "Slave Illegal Burst\n");
 
        if (status & XILINX_PCIE_INTR_MST_DECERR)
-               dev_warn(port->dev, "Master decode error\n");
+               dev_warn(dev, "Master decode error\n");
 
        if (status & XILINX_PCIE_INTR_MST_SLVERR)
-               dev_warn(port->dev, "Master slave error\n");
+               dev_warn(dev, "Master slave error\n");
 
        if (status & XILINX_PCIE_INTR_MST_ERRP)
-               dev_warn(port->dev, "Master error poison\n");
+               dev_warn(dev, "Master error poison\n");
 
 error:
        /* Clear the Interrupt Decode register */
@@ -554,10 +555,12 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
  */
 static void xilinx_pcie_init_port(struct xilinx_pcie_port *port)
 {
+       struct device *dev = port->dev;
+
        if (xilinx_pcie_link_is_up(port))
-               dev_info(port->dev, "PCIe Link is UP\n");
+               dev_info(dev, "PCIe Link is UP\n");
        else
-               dev_info(port->dev, "PCIe Link is DOWN\n");
+               dev_info(dev, "PCIe Link is DOWN\n");
 
        /* Disable all interrupts */
        pcie_write(port, ~XILINX_PCIE_IDR_ALL_MASK,
@@ -627,8 +630,8 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)
  */
 static int xilinx_pcie_probe(struct platform_device *pdev)
 {
-       struct xilinx_pcie_port *port;
        struct device *dev = &pdev->dev;
+       struct xilinx_pcie_port *port;
        struct pci_bus *bus;
        int err;
        resource_size_t iobase = 0;
@@ -668,15 +671,14 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
        if (err)
                goto error;
 
-       bus = pci_create_root_bus(&pdev->dev, 0,
-                                 &xilinx_pcie_ops, port, &res);
+       bus = pci_create_root_bus(dev, 0, &xilinx_pcie_ops, port, &res);
        if (!bus) {
                err = -ENOMEM;
                goto error;
        }
 
 #ifdef CONFIG_PCI_MSI
-       xilinx_pcie_msi_chip.dev = port->dev;
+       xilinx_pcie_msi_chip.dev = dev;
        bus->msi = &xilinx_pcie_msi_chip;
 #endif
        pci_scan_child_bus(bus);
@@ -685,8 +687,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
        pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
 #endif
        pci_bus_add_devices(bus);
-       platform_set_drvdata(pdev, port);
-
        return 0;
 
 error:
index e1ab864e1a7f06f7600a435ed4ed80e8513d5336..c8c72e8259d38bc47c83a4222c7311f6e97e7036 100644 (file)
@@ -151,21 +151,21 @@ FUNC_GROUP_DECL(GPID0, F19, E21);
 
 #define GPID2_DESC      SIG_DESC_SET(SCU8C, 9)
 
-#define D20 26
+#define F20 26
 SIG_EXPR_LIST_DECL_SINGLE(SD2DAT0, SD2, SD2_DESC);
 SIG_EXPR_DECL(GPID2IN, GPID2, GPID2_DESC);
 SIG_EXPR_DECL(GPID2IN, GPID, GPID_DESC);
 SIG_EXPR_LIST_DECL_DUAL(GPID2IN, GPID2, GPID);
-MS_PIN_DECL(D20, GPIOD2, SD2DAT0, GPID2IN);
+MS_PIN_DECL(F20, GPIOD2, SD2DAT0, GPID2IN);
 
-#define D21 27
+#define D20 27
 SIG_EXPR_LIST_DECL_SINGLE(SD2DAT1, SD2, SD2_DESC);
 SIG_EXPR_DECL(GPID2OUT, GPID2, GPID2_DESC);
 SIG_EXPR_DECL(GPID2OUT, GPID, GPID_DESC);
 SIG_EXPR_LIST_DECL_DUAL(GPID2OUT, GPID2, GPID);
-MS_PIN_DECL(D21, GPIOD3, SD2DAT1, GPID2OUT);
+MS_PIN_DECL(D20, GPIOD3, SD2DAT1, GPID2OUT);
 
-FUNC_GROUP_DECL(GPID2, D20, D21);
+FUNC_GROUP_DECL(GPID2, F20, D20);
 
 #define GPIE_DESC      SIG_DESC_SET(HW_STRAP1, 21)
 #define GPIE0_DESC     SIG_DESC_SET(SCU8C, 12)
@@ -182,28 +182,88 @@ SIG_EXPR_LIST_DECL_SINGLE(NDCD3, NDCD3, SIG_DESC_SET(SCU80, 17));
 SIG_EXPR_DECL(GPIE0OUT, GPIE0, GPIE0_DESC);
 SIG_EXPR_DECL(GPIE0OUT, GPIE, GPIE_DESC);
 SIG_EXPR_LIST_DECL_DUAL(GPIE0OUT, GPIE0, GPIE);
-MS_PIN_DECL(C20, GPIE0, NDCD3, GPIE0OUT);
+MS_PIN_DECL(C20, GPIOE1, NDCD3, GPIE0OUT);
 
 FUNC_GROUP_DECL(GPIE0, B20, C20);
 
-#define SPI1_DESC      SIG_DESC_SET(HW_STRAP1, 13)
+#define SPI1_DESC              { HW_STRAP1, GENMASK(13, 12), 1, 0 }
+#define SPI1DEBUG_DESC         { HW_STRAP1, GENMASK(13, 12), 2, 0 }
+#define SPI1PASSTHRU_DESC      { HW_STRAP1, GENMASK(13, 12), 3, 0 }
+
 #define C18 64
-SIG_EXPR_LIST_DECL_SINGLE(SYSCS, SPI1, COND1, SPI1_DESC);
+SIG_EXPR_DECL(SYSCS, SPI1DEBUG, COND1, SPI1DEBUG_DESC);
+SIG_EXPR_DECL(SYSCS, SPI1PASSTHRU, COND1, SPI1PASSTHRU_DESC);
+SIG_EXPR_LIST_DECL_DUAL(SYSCS, SPI1DEBUG, SPI1PASSTHRU);
 SS_PIN_DECL(C18, GPIOI0, SYSCS);
 
 #define E15 65
-SIG_EXPR_LIST_DECL_SINGLE(SYSCK, SPI1, COND1, SPI1_DESC);
+SIG_EXPR_DECL(SYSCK, SPI1DEBUG, COND1, SPI1DEBUG_DESC);
+SIG_EXPR_DECL(SYSCK, SPI1PASSTHRU, COND1, SPI1PASSTHRU_DESC);
+SIG_EXPR_LIST_DECL_DUAL(SYSCK, SPI1DEBUG, SPI1PASSTHRU);
 SS_PIN_DECL(E15, GPIOI1, SYSCK);
 
-#define A14 66
-SIG_EXPR_LIST_DECL_SINGLE(SYSMOSI, SPI1, COND1, SPI1_DESC);
-SS_PIN_DECL(A14, GPIOI2, SYSMOSI);
+#define B16 66
+SIG_EXPR_DECL(SYSMOSI, SPI1DEBUG, COND1, SPI1DEBUG_DESC);
+SIG_EXPR_DECL(SYSMOSI, SPI1PASSTHRU, COND1, SPI1PASSTHRU_DESC);
+SIG_EXPR_LIST_DECL_DUAL(SYSMOSI, SPI1DEBUG, SPI1PASSTHRU);
+SS_PIN_DECL(B16, GPIOI2, SYSMOSI);
 
 #define C16 67
-SIG_EXPR_LIST_DECL_SINGLE(SYSMISO, SPI1, COND1, SPI1_DESC);
+SIG_EXPR_DECL(SYSMISO, SPI1DEBUG, COND1, SPI1DEBUG_DESC);
+SIG_EXPR_DECL(SYSMISO, SPI1PASSTHRU, COND1, SPI1PASSTHRU_DESC);
+SIG_EXPR_LIST_DECL_DUAL(SYSMISO, SPI1DEBUG, SPI1PASSTHRU);
 SS_PIN_DECL(C16, GPIOI3, SYSMISO);
 
-FUNC_GROUP_DECL(SPI1, C18, E15, A14, C16);
+#define VB_DESC        SIG_DESC_SET(HW_STRAP1, 5)
+
+#define B15 68
+SIG_EXPR_DECL(SPI1CS0, SPI1, COND1, SPI1_DESC);
+SIG_EXPR_DECL(SPI1CS0, SPI1DEBUG, COND1, SPI1DEBUG_DESC);
+SIG_EXPR_DECL(SPI1CS0, SPI1PASSTHRU, COND1, SPI1PASSTHRU_DESC);
+SIG_EXPR_LIST_DECL(SPI1CS0, SIG_EXPR_PTR(SPI1CS0, SPI1),
+                           SIG_EXPR_PTR(SPI1CS0, SPI1DEBUG),
+                           SIG_EXPR_PTR(SPI1CS0, SPI1PASSTHRU));
+SIG_EXPR_LIST_DECL_SINGLE(VBCS, VGABIOSROM, COND1, VB_DESC);
+MS_PIN_DECL(B15, GPIOI4, SPI1CS0, VBCS);
+
+#define C15 69
+SIG_EXPR_DECL(SPI1CK, SPI1, COND1, SPI1_DESC);
+SIG_EXPR_DECL(SPI1CK, SPI1DEBUG, COND1, SPI1DEBUG_DESC);
+SIG_EXPR_DECL(SPI1CK, SPI1PASSTHRU, COND1, SPI1PASSTHRU_DESC);
+SIG_EXPR_LIST_DECL(SPI1CK, SIG_EXPR_PTR(SPI1CK, SPI1),
+                           SIG_EXPR_PTR(SPI1CK, SPI1DEBUG),
+                           SIG_EXPR_PTR(SPI1CK, SPI1PASSTHRU));
+SIG_EXPR_LIST_DECL_SINGLE(VBCK, VGABIOSROM, COND1, VB_DESC);
+MS_PIN_DECL(C15, GPIOI5, SPI1CK, VBCK);
+
+#define A14 70
+SIG_EXPR_DECL(SPI1MOSI, SPI1, COND1, SPI1_DESC);
+SIG_EXPR_DECL(SPI1MOSI, SPI1DEBUG, COND1, SPI1DEBUG_DESC);
+SIG_EXPR_DECL(SPI1MOSI, SPI1PASSTHRU, COND1, SPI1PASSTHRU_DESC);
+SIG_EXPR_LIST_DECL(SPI1MOSI, SIG_EXPR_PTR(SPI1MOSI, SPI1),
+                           SIG_EXPR_PTR(SPI1MOSI, SPI1DEBUG),
+                           SIG_EXPR_PTR(SPI1MOSI, SPI1PASSTHRU));
+SIG_EXPR_LIST_DECL_SINGLE(VBMOSI, VGABIOSROM, COND1, VB_DESC);
+MS_PIN_DECL(A14, GPIOI6, SPI1MOSI, VBMOSI);
+
+#define A15 71
+SIG_EXPR_DECL(SPI1MISO, SPI1, COND1, SPI1_DESC);
+SIG_EXPR_DECL(SPI1MISO, SPI1DEBUG, COND1, SPI1DEBUG_DESC);
+SIG_EXPR_DECL(SPI1MISO, SPI1PASSTHRU, COND1, SPI1PASSTHRU_DESC);
+SIG_EXPR_LIST_DECL(SPI1MISO, SIG_EXPR_PTR(SPI1MISO, SPI1),
+                           SIG_EXPR_PTR(SPI1MISO, SPI1DEBUG),
+                           SIG_EXPR_PTR(SPI1MISO, SPI1PASSTHRU));
+SIG_EXPR_LIST_DECL_SINGLE(VBMISO, VGABIOSROM, COND1, VB_DESC);
+MS_PIN_DECL(A15, GPIOI7, SPI1MISO, VBMISO);
+
+FUNC_GROUP_DECL(SPI1, B15, C15, A14, A15);
+FUNC_GROUP_DECL(SPI1DEBUG, C18, E15, B16, C16, B15, C15, A14, A15);
+FUNC_GROUP_DECL(SPI1PASSTHRU, C18, E15, B16, C16, B15, C15, A14, A15);
+FUNC_GROUP_DECL(VGABIOSROM, B15, C15, A14, A15);
+
+#define R2 72
+SIG_EXPR_LIST_DECL_SINGLE(SGPMCK, SGPM, SIG_DESC_SET(SCU84, 8));
+SS_PIN_DECL(R2, GPIOJ0, SGPMCK);
 
 #define L2 73
 SIG_EXPR_LIST_DECL_SINGLE(SGPMLD, SGPM, SIG_DESC_SET(SCU84, 9));
@@ -580,6 +640,7 @@ static struct pinctrl_pin_desc aspeed_g5_pins[ASPEED_G5_NR_PINS] = {
        ASPEED_PINCTRL_PIN(A12),
        ASPEED_PINCTRL_PIN(A13),
        ASPEED_PINCTRL_PIN(A14),
+       ASPEED_PINCTRL_PIN(A15),
        ASPEED_PINCTRL_PIN(A2),
        ASPEED_PINCTRL_PIN(A3),
        ASPEED_PINCTRL_PIN(A4),
@@ -592,6 +653,8 @@ static struct pinctrl_pin_desc aspeed_g5_pins[ASPEED_G5_NR_PINS] = {
        ASPEED_PINCTRL_PIN(B12),
        ASPEED_PINCTRL_PIN(B13),
        ASPEED_PINCTRL_PIN(B14),
+       ASPEED_PINCTRL_PIN(B15),
+       ASPEED_PINCTRL_PIN(B16),
        ASPEED_PINCTRL_PIN(B2),
        ASPEED_PINCTRL_PIN(B20),
        ASPEED_PINCTRL_PIN(B3),
@@ -603,6 +666,7 @@ static struct pinctrl_pin_desc aspeed_g5_pins[ASPEED_G5_NR_PINS] = {
        ASPEED_PINCTRL_PIN(C12),
        ASPEED_PINCTRL_PIN(C13),
        ASPEED_PINCTRL_PIN(C14),
+       ASPEED_PINCTRL_PIN(C15),
        ASPEED_PINCTRL_PIN(C16),
        ASPEED_PINCTRL_PIN(C18),
        ASPEED_PINCTRL_PIN(C2),
@@ -614,7 +678,6 @@ static struct pinctrl_pin_desc aspeed_g5_pins[ASPEED_G5_NR_PINS] = {
        ASPEED_PINCTRL_PIN(D10),
        ASPEED_PINCTRL_PIN(D2),
        ASPEED_PINCTRL_PIN(D20),
-       ASPEED_PINCTRL_PIN(D21),
        ASPEED_PINCTRL_PIN(D4),
        ASPEED_PINCTRL_PIN(D5),
        ASPEED_PINCTRL_PIN(D6),
@@ -630,6 +693,7 @@ static struct pinctrl_pin_desc aspeed_g5_pins[ASPEED_G5_NR_PINS] = {
        ASPEED_PINCTRL_PIN(E7),
        ASPEED_PINCTRL_PIN(E9),
        ASPEED_PINCTRL_PIN(F19),
+       ASPEED_PINCTRL_PIN(F20),
        ASPEED_PINCTRL_PIN(F9),
        ASPEED_PINCTRL_PIN(H20),
        ASPEED_PINCTRL_PIN(L1),
@@ -691,11 +755,14 @@ static const struct aspeed_pin_group aspeed_g5_groups[] = {
        ASPEED_PINCTRL_GROUP(RMII2),
        ASPEED_PINCTRL_GROUP(SD1),
        ASPEED_PINCTRL_GROUP(SPI1),
+       ASPEED_PINCTRL_GROUP(SPI1DEBUG),
+       ASPEED_PINCTRL_GROUP(SPI1PASSTHRU),
        ASPEED_PINCTRL_GROUP(TIMER4),
        ASPEED_PINCTRL_GROUP(TIMER5),
        ASPEED_PINCTRL_GROUP(TIMER6),
        ASPEED_PINCTRL_GROUP(TIMER7),
        ASPEED_PINCTRL_GROUP(TIMER8),
+       ASPEED_PINCTRL_GROUP(VGABIOSROM),
 };
 
 static const struct aspeed_pin_function aspeed_g5_functions[] = {
@@ -733,11 +800,14 @@ static const struct aspeed_pin_function aspeed_g5_functions[] = {
        ASPEED_PINCTRL_FUNC(RMII2),
        ASPEED_PINCTRL_FUNC(SD1),
        ASPEED_PINCTRL_FUNC(SPI1),
+       ASPEED_PINCTRL_FUNC(SPI1DEBUG),
+       ASPEED_PINCTRL_FUNC(SPI1PASSTHRU),
        ASPEED_PINCTRL_FUNC(TIMER4),
        ASPEED_PINCTRL_FUNC(TIMER5),
        ASPEED_PINCTRL_FUNC(TIMER6),
        ASPEED_PINCTRL_FUNC(TIMER7),
        ASPEED_PINCTRL_FUNC(TIMER8),
+       ASPEED_PINCTRL_FUNC(VGABIOSROM),
 };
 
 static struct aspeed_pinctrl_data aspeed_g5_pinctrl_data = {
index 0391f9f13f3e6cb0d15334e27f0d6d92733702a7..49aeba91253198c644794c23cb4877368f6905ad 100644 (file)
@@ -166,13 +166,9 @@ static bool aspeed_sig_expr_set(const struct aspeed_sig_expr *expr,
                                bool enable, struct regmap *map)
 {
        int i;
-       bool ret;
-
-       ret = aspeed_sig_expr_eval(expr, enable, map);
-       if (ret)
-               return ret;
 
        for (i = 0; i < expr->ndescs; i++) {
+               bool ret;
                const struct aspeed_sig_desc *desc = &expr->descs[i];
                u32 pattern = enable ? desc->enable : desc->disable;
 
@@ -199,12 +195,18 @@ static bool aspeed_sig_expr_set(const struct aspeed_sig_expr *expr,
 static bool aspeed_sig_expr_enable(const struct aspeed_sig_expr *expr,
                                   struct regmap *map)
 {
+       if (aspeed_sig_expr_eval(expr, true, map))
+               return true;
+
        return aspeed_sig_expr_set(expr, true, map);
 }
 
 static bool aspeed_sig_expr_disable(const struct aspeed_sig_expr *expr,
                                    struct regmap *map)
 {
+       if (!aspeed_sig_expr_eval(expr, true, map))
+               return true;
+
        return aspeed_sig_expr_set(expr, false, map);
 }
 
index d22a9fe2e6dfc36d63f9e493b61f2f35c2e21f97..71bbeb9321bad587504bf5ca4f4c889019b4b9f4 100644 (file)
@@ -1808,6 +1808,8 @@ static int byt_pinctrl_probe(struct platform_device *pdev)
                return PTR_ERR(vg->pctl_dev);
        }
 
+       raw_spin_lock_init(&vg->lock);
+
        ret = byt_gpio_probe(vg);
        if (ret) {
                pinctrl_unregister(vg->pctl_dev);
@@ -1815,7 +1817,6 @@ static int byt_pinctrl_probe(struct platform_device *pdev)
        }
 
        platform_set_drvdata(pdev, vg);
-       raw_spin_lock_init(&vg->lock);
        pm_runtime_enable(&pdev->dev);
 
        return 0;
index 63387a40b973a417d6dd00ec9fe952413465b159..01443762e57055b88ea9f1accb0cf6e10eda2978 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/pinctrl/pinconf.h>
 #include <linux/pinctrl/pinconf-generic.h>
 
+#include "../core.h"
 #include "pinctrl-intel.h"
 
 /* Offset from regs */
@@ -1056,6 +1057,26 @@ int intel_pinctrl_remove(struct platform_device *pdev)
 EXPORT_SYMBOL_GPL(intel_pinctrl_remove);
 
 #ifdef CONFIG_PM_SLEEP
+static bool intel_pinctrl_should_save(struct intel_pinctrl *pctrl, unsigned pin)
+{
+       const struct pin_desc *pd = pin_desc_get(pctrl->pctldev, pin);
+
+       if (!pd || !intel_pad_usable(pctrl, pin))
+               return false;
+
+       /*
+        * Only restore the pin if it is actually in use by the kernel (or
+        * by userspace). It is possible that some pins are used by the
+        * BIOS during resume and those are not always locked down so leave
+        * them alone.
+        */
+       if (pd->mux_owner || pd->gpio_owner ||
+           gpiochip_line_is_irq(&pctrl->chip, pin))
+               return true;
+
+       return false;
+}
+
 int intel_pinctrl_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -1069,7 +1090,7 @@ int intel_pinctrl_suspend(struct device *dev)
                const struct pinctrl_pin_desc *desc = &pctrl->soc->pins[i];
                u32 val;
 
-               if (!intel_pad_usable(pctrl, desc->number))
+               if (!intel_pinctrl_should_save(pctrl, desc->number))
                        continue;
 
                val = readl(intel_get_padcfg(pctrl, desc->number, PADCFG0));
@@ -1130,7 +1151,7 @@ int intel_pinctrl_resume(struct device *dev)
                void __iomem *padcfg;
                u32 val;
 
-               if (!intel_pad_usable(pctrl, desc->number))
+               if (!intel_pinctrl_should_save(pctrl, desc->number))
                        continue;
 
                padcfg = intel_get_padcfg(pctrl, desc->number, PADCFG0);
index 07462d79d04000685c946a19a9cef1d90526400d..1aba2c74160eb5c51ce49dd679de2dd0090fe2dd 100644 (file)
@@ -309,7 +309,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
                 * much memory to the process.
                 */
                down_read(&current->mm->mmap_sem);
-               ret = get_user_pages(address, 1, !is_write, 0, &page, NULL);
+               ret = get_user_pages(address, 1, is_write ? 0 : FOLL_WRITE,
+                               &page, NULL);
                up_read(&current->mm->mmap_sem);
                if (ret < 0)
                        break;
index 81b8dcca8891dc00ca5d0cb48c28329d25e70887..b8a21d7b25d4c34e4042caf624ac4c86211c11d0 100644 (file)
@@ -576,6 +576,7 @@ config ASUS_WMI
 config ASUS_NB_WMI
        tristate "Asus Notebook WMI Driver"
        depends on ASUS_WMI
+       depends on SERIO_I8042 || SERIO_I8042 = n
        ---help---
          This is a driver for newer Asus notebooks. It adds extra features
          like wireless radio and bluetooth control, leds, hotkeys, backlight...
index 460fa6708bfccbc6774284fc859a877d750d02b3..2acdb0d6ea8983f82ed23a28e2408ab37a6f0ce7 100644 (file)
@@ -405,7 +405,7 @@ static inline void acerhdf_enable_kernelmode(void)
        kernelmode = 1;
 
        thz_dev->polling_delay = interval*1000;
-       thermal_zone_device_update(thz_dev);
+       thermal_zone_device_update(thz_dev, THERMAL_EVENT_UNSPECIFIED);
        pr_notice("kernel mode fan control ON\n");
 }
 
index 15f1311465015c72fcc82210a15df0bb24532928..28551f5a2e0772ffc766f5df860a8cad55f75eeb 100644 (file)
@@ -932,30 +932,19 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(infos);
 
-static int parse_arg(const char *buf, unsigned long count, int *val)
-{
-       if (!count)
-               return 0;
-       if (count > 31)
-               return -EINVAL;
-       if (sscanf(buf, "%i", val) != 1)
-               return -EINVAL;
-       return count;
-}
-
 static ssize_t sysfs_acpi_set(struct asus_laptop *asus,
                              const char *buf, size_t count,
                              const char *method)
 {
        int rv, value;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv <= 0)
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
                return rv;
 
        if (write_acpi_int(asus->handle, method, value))
                return -ENODEV;
-       return rv;
+       return count;
 }
 
 /*
@@ -975,15 +964,17 @@ static ssize_t ledd_store(struct device *dev, struct device_attribute *attr,
        struct asus_laptop *asus = dev_get_drvdata(dev);
        int rv, value;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0) {
-               if (write_acpi_int(asus->handle, METHOD_LEDD, value)) {
-                       pr_warn("LED display write failed\n");
-                       return -ENODEV;
-               }
-               asus->ledd_status = (u32) value;
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
+               return rv;
+
+       if (write_acpi_int(asus->handle, METHOD_LEDD, value)) {
+               pr_warn("LED display write failed\n");
+               return -ENODEV;
        }
-       return rv;
+
+       asus->ledd_status = (u32) value;
+       return count;
 }
 static DEVICE_ATTR_RW(ledd);
 
@@ -1148,10 +1139,12 @@ static ssize_t display_store(struct device *dev, struct device_attribute *attr,
        struct asus_laptop *asus = dev_get_drvdata(dev);
        int rv, value;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0)
-               asus_set_display(asus, value);
-       return rv;
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
+               return rv;
+
+       asus_set_display(asus, value);
+       return count;
 }
 static DEVICE_ATTR_WO(display);
 
@@ -1190,11 +1183,12 @@ static ssize_t ls_switch_store(struct device *dev,
        struct asus_laptop *asus = dev_get_drvdata(dev);
        int rv, value;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0)
-               asus_als_switch(asus, value ? 1 : 0);
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
+               return rv;
 
-       return rv;
+       asus_als_switch(asus, value ? 1 : 0);
+       return count;
 }
 static DEVICE_ATTR_RW(ls_switch);
 
@@ -1219,14 +1213,15 @@ static ssize_t ls_level_store(struct device *dev, struct device_attribute *attr,
        struct asus_laptop *asus = dev_get_drvdata(dev);
        int rv, value;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0) {
-               value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
-               /* 0 <= value <= 15 */
-               asus_als_level(asus, value);
-       }
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
+               return rv;
+
+       value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
+       /* 0 <= value <= 15 */
+       asus_als_level(asus, value);
 
-       return rv;
+       return count;
 }
 static DEVICE_ATTR_RW(ls_level);
 
@@ -1301,14 +1296,14 @@ static ssize_t gps_store(struct device *dev, struct device_attribute *attr,
        int rv, value;
        int ret;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv <= 0)
-               return -EINVAL;
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
+               return rv;
        ret = asus_gps_switch(asus, !!value);
        if (ret)
                return ret;
        rfkill_set_sw_state(asus->gps.rfkill, !value);
-       return rv;
+       return count;
 }
 static DEVICE_ATTR_RW(gps);
 
index adecc1c555f02a8b18eadca62c0e6eb5e7796b38..26e4cbc34db8f8e3b515f2e4e3a2c0c836e59c4b 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/input/sparse-keymap.h>
 #include <linux/fb.h>
 #include <linux/dmi.h>
+#include <linux/i8042.h>
 
 #include "asus-wmi.h"
 
@@ -55,10 +56,34 @@ MODULE_PARM_DESC(wapf, "WAPF value");
 
 static struct quirk_entry *quirks;
 
+static bool asus_q500a_i8042_filter(unsigned char data, unsigned char str,
+                             struct serio *port)
+{
+       static bool extended;
+       bool ret = false;
+
+       if (str & I8042_STR_AUXDATA)
+               return false;
+
+       if (unlikely(data == 0xe1)) {
+               extended = true;
+               ret = true;
+       } else if (unlikely(extended)) {
+               extended = false;
+               ret = true;
+       }
+
+       return ret;
+}
+
 static struct quirk_entry quirk_asus_unknown = {
        .wapf = 0,
 };
 
+static struct quirk_entry quirk_asus_q500a = {
+       .i8042_filter = asus_q500a_i8042_filter,
+};
+
 /*
  * For those machines that need software to control bt/wifi status
  * and can't adjust brightness through ACPI interface
@@ -87,6 +112,10 @@ static struct quirk_entry quirk_no_rfkill_wapf4 = {
        .no_rfkill = true,
 };
 
+static struct quirk_entry quirk_asus_ux303ub = {
+       .wmi_backlight_native = true,
+};
+
 static int dmi_matched(const struct dmi_system_id *dmi)
 {
        quirks = dmi->driver_data;
@@ -94,6 +123,15 @@ static int dmi_matched(const struct dmi_system_id *dmi)
 }
 
 static const struct dmi_system_id asus_quirks[] = {
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. Q500A",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Q500A"),
+               },
+               .driver_data = &quirk_asus_q500a,
+       },
        {
                .callback = dmi_matched,
                .ident = "ASUSTeK COMPUTER INC. U32U",
@@ -351,11 +389,22 @@ static const struct dmi_system_id asus_quirks[] = {
                },
                .driver_data = &quirk_no_rfkill,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. UX303UB",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "UX303UB"),
+               },
+               .driver_data = &quirk_asus_ux303ub,
+       },
        {},
 };
 
 static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
 {
+       int ret;
+
        quirks = &quirk_asus_unknown;
        dmi_check_system(asus_quirks);
 
@@ -367,6 +416,15 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
                quirks->wapf = wapf;
        else
                wapf = quirks->wapf;
+
+       if (quirks->i8042_filter) {
+               ret = i8042_install_filter(quirks->i8042_filter);
+               if (ret) {
+                       pr_warn("Unable to install key filter\n");
+                       return;
+               }
+               pr_info("Using i8042 filter function for receiving events\n");
+       }
 }
 
 static const struct key_entry asus_nb_wmi_keymap[] = {
index 7c093a0b78bb995f687a47329011a40c7ff2a9f4..ce6ca31a2d09a207c1e80c157427aa3960079b73 100644 (file)
@@ -2084,6 +2084,9 @@ static int asus_wmi_add(struct platform_device *pdev)
        if (asus->driver->quirks->wmi_backlight_power)
                acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
 
+       if (asus->driver->quirks->wmi_backlight_native)
+               acpi_video_set_dmi_backlight_type(acpi_backlight_native);
+
        if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                err = asus_wmi_backlight_init(asus);
                if (err && err != -ENODEV)
index 5de1df510ebd8c3ed39db046e70daf5155a4a7ce..0e19014e9f5420415824d79a4aeca426eccc9a8c 100644 (file)
@@ -28,6 +28,7 @@
 #define _ASUS_WMI_H_
 
 #include <linux/platform_device.h>
+#include <linux/i8042.h>
 
 #define ASUS_WMI_KEY_IGNORE (-1)
 #define ASUS_WMI_BRN_DOWN      0x20
@@ -43,6 +44,7 @@ struct quirk_entry {
        bool scalar_panel_brightness;
        bool store_backlight_power;
        bool wmi_backlight_power;
+       bool wmi_backlight_native;
        int wapf;
        /*
         * For machines with AMD graphic chips, it will send out WMI event
@@ -51,6 +53,9 @@ struct quirk_entry {
         * and let the ACPI interrupt to send out the key event.
         */
        int no_display_toggle;
+
+       bool (*i8042_filter)(unsigned char data, unsigned char str,
+                            struct serio *serio);
 };
 
 struct asus_wmi_driver {
index d1a091b93192c7dc4a20d8dbf9901d65d918e1e1..a2323941e67707711f6e2c2738057d2f21744e4d 100644 (file)
@@ -933,6 +933,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 900"),
                },
        },
+       {
+               .ident = "Lenovo YOGA 910-13IKB",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 910-13IKB"),
+               },
+       },
        {}
 };
 
index 520b58a04daa8d898a2e7eb4277a55b7d752a5ce..e8b1b836ca2d3cd04e884e26a1916bc8a450daec 100644 (file)
@@ -100,7 +100,7 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
        struct dentry *dir, *file;
 
        dir = debugfs_create_dir("pmc_core", NULL);
-       if (IS_ERR_OR_NULL(dir))
+       if (!dir)
                return -ENOMEM;
 
        pmcdev->dbgfs_dir = dir;
index a511d518206ba274fdece09425d0983f98dedbe2..0bf51d574fa9a47ada5e6e0b3c98d6cb4d710581 100644 (file)
@@ -522,48 +522,36 @@ static struct resource telemetry_res[] = {
 static int ipc_create_punit_device(void)
 {
        struct platform_device *pdev;
-       int ret;
-
-       pdev = platform_device_alloc(PUNIT_DEVICE_NAME, -1);
-       if (!pdev) {
-               dev_err(ipcdev.dev, "Failed to alloc punit platform device\n");
-               return -ENOMEM;
-       }
-
-       pdev->dev.parent = ipcdev.dev;
-       ret = platform_device_add_resources(pdev, punit_res_array,
-                                           ARRAY_SIZE(punit_res_array));
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add platform punit resources\n");
-               goto err;
-       }
+       const struct platform_device_info pdevinfo = {
+               .parent = ipcdev.dev,
+               .name = PUNIT_DEVICE_NAME,
+               .id = -1,
+               .res = punit_res_array,
+               .num_res = ARRAY_SIZE(punit_res_array),
+               };
+
+       pdev = platform_device_register_full(&pdevinfo);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
 
-       ret = platform_device_add(pdev);
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add punit platform device\n");
-               goto err;
-       }
        ipcdev.punit_dev = pdev;
 
        return 0;
-err:
-       platform_device_put(pdev);
-       return ret;
 }
 
 static int ipc_create_tco_device(void)
 {
        struct platform_device *pdev;
        struct resource *res;
-       int ret;
-
-       pdev = platform_device_alloc(TCO_DEVICE_NAME, -1);
-       if (!pdev) {
-               dev_err(ipcdev.dev, "Failed to alloc tco platform device\n");
-               return -ENOMEM;
-       }
-
-       pdev->dev.parent = ipcdev.dev;
+       const struct platform_device_info pdevinfo = {
+               .parent = ipcdev.dev,
+               .name = TCO_DEVICE_NAME,
+               .id = -1,
+               .res = tco_res,
+               .num_res = ARRAY_SIZE(tco_res),
+               .data = &tco_info,
+               .size_data = sizeof(tco_info),
+               };
 
        res = tco_res + TCO_RESOURCE_ACPI_IO;
        res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET;
@@ -577,45 +565,26 @@ static int ipc_create_tco_device(void)
        res->start = ipcdev.gcr_base + TCO_PMC_OFFSET;
        res->end = res->start + TCO_PMC_SIZE - 1;
 
-       ret = platform_device_add_resources(pdev, tco_res, ARRAY_SIZE(tco_res));
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add tco platform resources\n");
-               goto err;
-       }
+       pdev = platform_device_register_full(&pdevinfo);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
 
-       ret = platform_device_add_data(pdev, &tco_info, sizeof(tco_info));
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add tco platform data\n");
-               goto err;
-       }
-
-       ret = platform_device_add(pdev);
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add tco platform device\n");
-               goto err;
-       }
        ipcdev.tco_dev = pdev;
 
        return 0;
-err:
-       platform_device_put(pdev);
-       return ret;
 }
 
 static int ipc_create_telemetry_device(void)
 {
        struct platform_device *pdev;
        struct resource *res;
-       int ret;
-
-       pdev = platform_device_alloc(TELEMETRY_DEVICE_NAME, -1);
-       if (!pdev) {
-               dev_err(ipcdev.dev,
-                       "Failed to allocate telemetry platform device\n");
-               return -ENOMEM;
-       }
-
-       pdev->dev.parent = ipcdev.dev;
+       const struct platform_device_info pdevinfo = {
+               .parent = ipcdev.dev,
+               .name = TELEMETRY_DEVICE_NAME,
+               .id = -1,
+               .res = telemetry_res,
+               .num_res = ARRAY_SIZE(telemetry_res),
+               };
 
        res = telemetry_res + TELEMETRY_RESOURCE_PUNIT_SSRAM;
        res->start = ipcdev.telem_punit_ssram_base;
@@ -625,26 +594,13 @@ static int ipc_create_telemetry_device(void)
        res->start = ipcdev.telem_pmc_ssram_base;
        res->end = res->start + ipcdev.telem_pmc_ssram_size - 1;
 
-       ret = platform_device_add_resources(pdev, telemetry_res,
-                                           ARRAY_SIZE(telemetry_res));
-       if (ret) {
-               dev_err(ipcdev.dev,
-                       "Failed to add telemetry platform resources\n");
-               goto err;
-       }
+       pdev = platform_device_register_full(&pdevinfo);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
 
-       ret = platform_device_add(pdev);
-       if (ret) {
-               dev_err(ipcdev.dev,
-                       "Failed to add telemetry platform device\n");
-               goto err;
-       }
        ipcdev.telemetry_dev = pdev;
 
        return 0;
-err:
-       platform_device_put(pdev);
-       return ret;
 }
 
 static int ipc_create_pmc_devices(void)
index 9d60a40d8b3fbd63a326271305a61c6f6675825a..074bf2fa1c550933651e1ef899bd932c38abfb88 100644 (file)
@@ -321,10 +321,9 @@ static int write_acpi_int(const char *methodName, int val)
 static acpi_status tci_raw(struct toshiba_acpi_dev *dev,
                           const u32 in[TCI_WORDS], u32 out[TCI_WORDS])
 {
+       union acpi_object in_objs[TCI_WORDS], out_objs[TCI_WORDS + 1];
        struct acpi_object_list params;
-       union acpi_object in_objs[TCI_WORDS];
        struct acpi_buffer results;
-       union acpi_object out_objs[TCI_WORDS + 1];
        acpi_status status;
        int i;
 
@@ -387,9 +386,8 @@ static int sci_open(struct toshiba_acpi_dev *dev)
 {
        u32 in[TCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 };
        u32 out[TCI_WORDS];
-       acpi_status status;
+       acpi_status status = tci_raw(dev, in, out);
 
-       status = tci_raw(dev, in, out);
        if  (ACPI_FAILURE(status)) {
                pr_err("ACPI call to open SCI failed\n");
                return 0;
@@ -425,9 +423,8 @@ static void sci_close(struct toshiba_acpi_dev *dev)
 {
        u32 in[TCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 };
        u32 out[TCI_WORDS];
-       acpi_status status;
+       acpi_status status = tci_raw(dev, in, out);
 
-       status = tci_raw(dev, in, out);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to close SCI failed\n");
                return;
@@ -479,10 +476,15 @@ static void toshiba_illumination_available(struct toshiba_acpi_dev *dev)
 
        status = tci_raw(dev, in, out);
        sci_close(dev);
-       if (ACPI_FAILURE(status))
+       if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to query Illumination support failed\n");
-       else if (out[0] == TOS_SUCCESS)
-               dev->illumination_supported = 1;
+               return;
+       }
+
+       if (out[0] != TOS_SUCCESS)
+               return;
+
+       dev->illumination_supported = 1;
 }
 
 static void toshiba_illumination_set(struct led_classdev *cdev,
@@ -509,7 +511,8 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
 {
        struct toshiba_acpi_dev *dev = container_of(cdev,
                        struct toshiba_acpi_dev, led_dev);
-       u32 state, result;
+       u32 result;
+       u32 state;
 
        /* First request : initialize communication. */
        if (!sci_open(dev))
@@ -546,24 +549,28 @@ static void toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev)
        sci_close(dev);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to query kbd illumination support failed\n");
-       } else if (out[0] == TOS_SUCCESS) {
-               /*
-                * Check for keyboard backlight timeout max value,
-                * previous kbd backlight implementation set this to
-                * 0x3c0003, and now the new implementation set this
-                * to 0x3c001a, use this to distinguish between them.
-                */
-               if (out[3] == SCI_KBD_TIME_MAX)
-                       dev->kbd_type = 2;
-               else
-                       dev->kbd_type = 1;
-               /* Get the current keyboard backlight mode */
-               dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK;
-               /* Get the current time (1-60 seconds) */
-               dev->kbd_time = out[2] >> HCI_MISC_SHIFT;
-               /* Flag as supported */
-               dev->kbd_illum_supported = 1;
+               return;
        }
+
+       if (out[0] != TOS_SUCCESS)
+               return;
+
+       /*
+        * Check for keyboard backlight timeout max value,
+        * previous kbd backlight implementation set this to
+        * 0x3c0003, and now the new implementation set this
+        * to 0x3c001a, use this to distinguish between them.
+        */
+       if (out[3] == SCI_KBD_TIME_MAX)
+               dev->kbd_type = 2;
+       else
+               dev->kbd_type = 1;
+       /* Get the current keyboard backlight mode */
+       dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK;
+       /* Get the current time (1-60 seconds) */
+       dev->kbd_time = out[2] >> HCI_MISC_SHIFT;
+       /* Flag as supported */
+       dev->kbd_illum_supported = 1;
 }
 
 static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
@@ -672,9 +679,9 @@ static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state)
 /* Eco Mode support */
 static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
 {
-       acpi_status status;
        u32 in[TCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 0, 0, 0 };
        u32 out[TCI_WORDS];
+       acpi_status status;
 
        dev->eco_supported = 0;
        dev->eco_led_registered = false;
@@ -682,7 +689,10 @@ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
        status = tci_raw(dev, in, out);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get ECO led failed\n");
-       } else if (out[0] == TOS_INPUT_DATA_ERROR) {
+               return;
+       }
+
+       if (out[0] == TOS_INPUT_DATA_ERROR) {
                /*
                 * If we receive 0x8300 (Input Data Error), it means that the
                 * LED device is present, but that we just screwed the input
@@ -694,10 +704,15 @@ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
                 */
                in[3] = 1;
                status = tci_raw(dev, in, out);
-               if (ACPI_FAILURE(status))
+               if (ACPI_FAILURE(status)) {
                        pr_err("ACPI call to get ECO led failed\n");
-               else if (out[0] == TOS_SUCCESS)
-                       dev->eco_supported = 1;
+                       return;
+               }
+
+               if (out[0] != TOS_SUCCESS)
+                       return;
+
+               dev->eco_supported = 1;
        }
 }
 
@@ -714,10 +729,11 @@ toshiba_eco_mode_get_status(struct led_classdev *cdev)
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get ECO led failed\n");
                return LED_OFF;
-       } else if (out[0] != TOS_SUCCESS) {
-               return LED_OFF;
        }
 
+       if (out[0] != TOS_SUCCESS)
+               return LED_OFF;
+
        return out[2] ? LED_FULL : LED_OFF;
 }
 
@@ -751,10 +767,15 @@ static void toshiba_accelerometer_available(struct toshiba_acpi_dev *dev)
         * this call also serves as initialization
         */
        status = tci_raw(dev, in, out);
-       if (ACPI_FAILURE(status))
+       if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to query the accelerometer failed\n");
-       else if (out[0] == TOS_SUCCESS)
-               dev->accelerometer_supported = 1;
+               return;
+       }
+
+       if (out[0] != TOS_SUCCESS)
+               return;
+
+       dev->accelerometer_supported = 1;
 }
 
 static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
@@ -769,15 +790,18 @@ static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to query the accelerometer failed\n");
                return -EIO;
-       } else if (out[0] == TOS_NOT_SUPPORTED) {
-               return -ENODEV;
-       } else if (out[0] == TOS_SUCCESS) {
-               *xy = out[2];
-               *z = out[4];
-               return 0;
        }
 
-       return -EIO;
+       if (out[0] == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       if (out[0] != TOS_SUCCESS)
+               return -EIO;
+
+       *xy = out[2];
+       *z = out[4];
+
+       return 0;
 }
 
 /* Sleep (Charge and Music) utilities support */
@@ -797,24 +821,29 @@ static void toshiba_usb_sleep_charge_available(struct toshiba_acpi_dev *dev)
                pr_err("ACPI call to get USB Sleep and Charge mode failed\n");
                sci_close(dev);
                return;
-       } else if (out[0] == TOS_NOT_SUPPORTED) {
+       }
+
+       if (out[0] != TOS_SUCCESS) {
                sci_close(dev);
                return;
-       } else if (out[0] == TOS_SUCCESS) {
-               dev->usbsc_mode_base = out[4];
        }
 
+       dev->usbsc_mode_base = out[4];
+
        in[5] = SCI_USB_CHARGE_BAT_LVL;
        status = tci_raw(dev, in, out);
        sci_close(dev);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get USB Sleep and Charge mode failed\n");
-       } else if (out[0] == TOS_SUCCESS) {
-               dev->usbsc_bat_level = out[2];
-               /* Flag as supported */
-               dev->usb_sleep_charge_supported = 1;
+               return;
        }
 
+       if (out[0] != TOS_SUCCESS)
+               return;
+
+       dev->usbsc_bat_level = out[2];
+       /* Flag as supported */
+       dev->usb_sleep_charge_supported = 1;
 }
 
 static int toshiba_usb_sleep_charge_get(struct toshiba_acpi_dev *dev,
@@ -868,14 +897,19 @@ static int toshiba_sleep_functions_status_get(struct toshiba_acpi_dev *dev,
        sci_close(dev);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get USB S&C battery level failed\n");
-       } else if (out[0] == TOS_NOT_SUPPORTED) {
-               return -ENODEV;
-       } else if (out[0] == TOS_SUCCESS) {
-               *mode = out[2];
-               return 0;
+               return -EIO;
        }
 
-       return -EIO;
+       if (out[0] == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       if (out[0] != TOS_SUCCESS)
+               return -EIO;
+
+       *mode = out[2];
+
+       return 0;
+
 }
 
 static int toshiba_sleep_functions_status_set(struct toshiba_acpi_dev *dev,
@@ -892,9 +926,12 @@ static int toshiba_sleep_functions_status_set(struct toshiba_acpi_dev *dev,
        in[5] = SCI_USB_CHARGE_BAT_LVL;
        status = tci_raw(dev, in, out);
        sci_close(dev);
-       if (ACPI_FAILURE(status))
+       if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to set USB S&C battery level failed\n");
-       else if (out[0] == TOS_NOT_SUPPORTED)
+               return -EIO;
+       }
+
+       if (out[0] == TOS_NOT_SUPPORTED)
                return -ENODEV;
 
        return out[0] == TOS_SUCCESS ? 0 : -EIO;
@@ -915,14 +952,18 @@ static int toshiba_usb_rapid_charge_get(struct toshiba_acpi_dev *dev,
        sci_close(dev);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get USB Rapid Charge failed\n");
-       } else if (out[0] == TOS_NOT_SUPPORTED) {
-               return -ENODEV;
-       } else if (out[0] == TOS_SUCCESS || out[0] == TOS_SUCCESS2) {
-               *state = out[2];
-               return 0;
+               return -EIO;
        }
 
-       return -EIO;
+       if (out[0] == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2)
+               return -EIO;
+
+       *state = out[2];
+
+       return 0;
 }
 
 static int toshiba_usb_rapid_charge_set(struct toshiba_acpi_dev *dev,
@@ -939,9 +980,12 @@ static int toshiba_usb_rapid_charge_set(struct toshiba_acpi_dev *dev,
        in[5] = SCI_USB_CHARGE_RAPID_DSP;
        status = tci_raw(dev, in, out);
        sci_close(dev);
-       if (ACPI_FAILURE(status))
+       if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to set USB Rapid Charge failed\n");
-       else if (out[0] == TOS_NOT_SUPPORTED)
+               return -EIO;
+       }
+
+       if (out[0] == TOS_NOT_SUPPORTED)
                return -ENODEV;
 
        return (out[0] == TOS_SUCCESS || out[0] == TOS_SUCCESS2) ? 0 : -EIO;
@@ -1097,14 +1141,18 @@ static int toshiba_hotkey_event_type_get(struct toshiba_acpi_dev *dev,
        status = tci_raw(dev, in, out);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get System type failed\n");
-       } else if (out[0] == TOS_NOT_SUPPORTED) {
-               return -ENODEV;
-       } else if (out[0] == TOS_SUCCESS) {
-               *type = out[3];
-               return 0;
+               return -EIO;
        }
 
-       return -EIO;
+       if (out[0] == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       if (out[0] != TOS_SUCCESS)
+               return -EIO;
+
+       *type = out[3];
+
+       return 0;
 }
 
 /* Wireless status (RFKill, WLAN, BT, WWAN) */
@@ -1154,7 +1202,6 @@ static void toshiba_wwan_available(struct toshiba_acpi_dev *dev)
         */
        in[3] = HCI_WIRELESS_WWAN;
        status = tci_raw(dev, in, out);
-
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get WWAN status failed\n");
                return;
@@ -1174,7 +1221,6 @@ static int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state)
 
        in[3] = HCI_WIRELESS_WWAN_STATUS;
        status = tci_raw(dev, in, out);
-
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to set WWAN status failed\n");
                return -EIO;
@@ -1193,7 +1239,6 @@ static int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state)
         */
        in[3] = HCI_WIRELESS_WWAN_POWER;
        status = tci_raw(dev, in, out);
-
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to set WWAN power failed\n");
                return -EIO;
@@ -1216,8 +1261,10 @@ static void toshiba_cooling_method_available(struct toshiba_acpi_dev *dev)
        dev->max_cooling_method = 0;
 
        status = tci_raw(dev, in, out);
-       if (ACPI_FAILURE(status))
+       if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get Cooling Method failed\n");
+               return;
+       }
 
        if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2)
                return;
@@ -1244,7 +1291,7 @@ static int toshiba_cooling_method_set(struct toshiba_acpi_dev *dev, u32 state)
        u32 result = hci_write(dev, HCI_COOLING_METHOD, state);
 
        if (result == TOS_FAILURE)
-               pr_err("ACPI call to get Cooling Method failed\n");
+               pr_err("ACPI call to set Cooling Method failed\n");
 
        if (result == TOS_NOT_SUPPORTED)
                return -ENODEV;
@@ -1282,9 +1329,9 @@ static struct proc_dir_entry *toshiba_proc_dir;
 /* LCD Brightness */
 static int __get_lcd_brightness(struct toshiba_acpi_dev *dev)
 {
+       int brightness = 0;
        u32 result;
        u32 value;
-       int brightness = 0;
 
        if (dev->tr_backlight_supported) {
                int ret = get_tr_backlight_status(dev, &value);
@@ -1301,10 +1348,10 @@ static int __get_lcd_brightness(struct toshiba_acpi_dev *dev)
                pr_err("ACPI call to get LCD Brightness failed\n");
        else if (result == TOS_NOT_SUPPORTED)
                return -ENODEV;
-       if (result == TOS_SUCCESS)
-               return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT);
 
-       return -EIO;
+       return result == TOS_SUCCESS ?
+                       brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT) :
+                       -EIO;
 }
 
 static int get_lcd_brightness(struct backlight_device *bd)
@@ -1325,15 +1372,15 @@ static int lcd_proc_show(struct seq_file *m, void *v)
 
        levels = dev->backlight_dev->props.max_brightness + 1;
        value = get_lcd_brightness(dev->backlight_dev);
-       if (value >= 0) {
-               seq_printf(m, "brightness:              %d\n", value);
-               seq_printf(m, "brightness_levels:       %d\n", levels);
-               return 0;
+       if (value < 0) {
+               pr_err("Error reading LCD brightness\n");
+               return value;
        }
 
-       pr_err("Error reading LCD brightness\n");
+       seq_printf(m, "brightness:              %d\n", value);
+       seq_printf(m, "brightness_levels:       %d\n", levels);
 
-       return -EIO;
+       return 0;
 }
 
 static int lcd_proc_open(struct inode *inode, struct file *file)
@@ -1377,7 +1424,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
        struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
        char cmd[42];
        size_t len;
-       int levels = dev->backlight_dev->props.max_brightness + 1;
+       int levels;
        int value;
 
        len = min(count, sizeof(cmd) - 1);
@@ -1385,6 +1432,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
                return -EFAULT;
        cmd[len] = '\0';
 
+       levels = dev->backlight_dev->props.max_brightness + 1;
        if (sscanf(cmd, " brightness : %i", &value) != 1 &&
            value < 0 && value > levels)
                return -EINVAL;
@@ -1420,20 +1468,21 @@ static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status)
 static int video_proc_show(struct seq_file *m, void *v)
 {
        struct toshiba_acpi_dev *dev = m->private;
+       int is_lcd, is_crt, is_tv;
        u32 value;
 
-       if (!get_video_status(dev, &value)) {
-               int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
-               int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
-               int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
+       if (get_video_status(dev, &value))
+               return -EIO;
 
-               seq_printf(m, "lcd_out:                 %d\n", is_lcd);
-               seq_printf(m, "crt_out:                 %d\n", is_crt);
-               seq_printf(m, "tv_out:                  %d\n", is_tv);
-               return 0;
-       }
+       is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
+       is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
+       is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
 
-       return -EIO;
+       seq_printf(m, "lcd_out:                 %d\n", is_lcd);
+       seq_printf(m, "crt_out:                 %d\n", is_crt);
+       seq_printf(m, "tv_out:                  %d\n", is_tv);
+
+       return 0;
 }
 
 static int video_proc_open(struct inode *inode, struct file *file)
@@ -1447,10 +1496,8 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
        struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
        char *buffer;
        char *cmd;
+       int lcd_out, crt_out, tv_out;
        int remain = count;
-       int lcd_out = -1;
-       int crt_out = -1;
-       int tv_out = -1;
        int value;
        int ret;
        u32 video_out;
@@ -1486,6 +1533,7 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
 
        kfree(cmd);
 
+       lcd_out = crt_out = tv_out = -1;
        ret = get_video_status(dev, &video_out);
        if (!ret) {
                unsigned int new_video_out = video_out;
@@ -1980,8 +2028,8 @@ static ssize_t usb_sleep_charge_store(struct device *dev,
                                      const char *buf, size_t count)
 {
        struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
-       u32 mode;
        int state;
+       u32 mode;
        int ret;
 
        ret = kstrtoint(buf, 0, &state);
@@ -2021,9 +2069,8 @@ static ssize_t sleep_functions_on_battery_show(struct device *dev,
                                               char *buf)
 {
        struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+       int bat_lvl, status;
        u32 state;
-       int bat_lvl;
-       int status;
        int ret;
        int tmp;
 
index 5db495dd018e9595fac55f159e6f5a2e5fcd9100..be1d137c60797e96afb3eae9fc4f1c61d0ddf42c 100644 (file)
@@ -80,7 +80,9 @@ static int toshiba_bluetooth_present(acpi_handle handle)
        if (ACPI_FAILURE(result)) {
                pr_err("ACPI call to query Bluetooth presence failed\n");
                return -ENXIO;
-       } else if (!bt_present) {
+       }
+
+       if (!bt_present) {
                pr_info("Bluetooth device not present\n");
                return -ENODEV;
        }
index 7f2afc6b5eb9539ffb3fb61cc9abdfbd212caacb..b3dec521e2b650069dbb54f2f064fbb4e1d1c5db 100644 (file)
@@ -59,7 +59,7 @@ static int toshiba_haps_protection_level(acpi_handle handle, int level)
                return -EIO;
        }
 
-       pr_info("HDD protection level set to: %d\n", level);
+       pr_debug("HDD protection level set to: %d\n", level);
 
        return 0;
 }
@@ -141,7 +141,7 @@ static struct attribute_group haps_attr_group = {
  */
 static void toshiba_haps_notify(struct acpi_device *device, u32 event)
 {
-       pr_info("Received event: 0x%x", event);
+       pr_debug("Received event: 0x%x", event);
 
        acpi_bus_generate_netlink_event(device->pnp.device_class,
                                        dev_name(&device->dev),
@@ -168,9 +168,13 @@ static int toshiba_haps_available(acpi_handle handle)
         * A non existent device as well as having (only)
         * Solid State Drives can cause the call to fail.
         */
-       status = acpi_evaluate_integer(handle, "_STA", NULL,
-                                      &hdd_present);
-       if (ACPI_FAILURE(status) || !hdd_present) {
+       status = acpi_evaluate_integer(handle, "_STA", NULL, &hdd_present);
+       if (ACPI_FAILURE(status)) {
+               pr_err("ACPI call to query HDD protection failed\n");
+               return 0;
+       }
+
+       if (!hdd_present) {
                pr_info("HDD protection not available or using SSD\n");
                return 0;
        }
index d637c933c8a90655ffe152101dda2b9c71fd3836..58a97d4205723fff7e25f454480169b6f658b0a4 100644 (file)
@@ -193,6 +193,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
                if (err)
                        break;
 
+               memset(&precise_offset, 0, sizeof(precise_offset));
                ts = ktime_to_timespec64(xtstamp.device);
                precise_offset.device.sec = ts.tv_sec;
                precise_offset.device.nsec = ts.tv_nsec;
index 80a566a00d0437dcc8fca7715c1109e7c2d35f67..bf0128899c094801f2b35c453725c02aae0fea39 100644 (file)
@@ -262,6 +262,15 @@ config PWM_LPSS_PLATFORM
          To compile this driver as a module, choose M here: the module
          will be called pwm-lpss-platform.
 
+config PWM_MESON
+       tristate "Amlogic Meson PWM driver"
+       depends on ARCH_MESON
+       help
+         The platform driver for Amlogic Meson PWM controller.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-meson.
+
 config PWM_MTK_DISP
        tristate "MediaTek display PWM driver"
        depends on ARCH_MEDIATEK || COMPILE_TEST
index feef1dd29f731a187b43bb1af7cd74be15f8e231..1194c54efcc2571cd9bfa7cfe976d22c06d44ca6 100644 (file)
@@ -24,6 +24,7 @@ obj-$(CONFIG_PWM_LPC32XX)     += pwm-lpc32xx.o
 obj-$(CONFIG_PWM_LPSS)         += pwm-lpss.o
 obj-$(CONFIG_PWM_LPSS_PCI)     += pwm-lpss-pci.o
 obj-$(CONFIG_PWM_LPSS_PLATFORM)        += pwm-lpss-platform.o
+obj-$(CONFIG_PWM_MESON)                += pwm-meson.o
 obj-$(CONFIG_PWM_MTK_DISP)     += pwm-mtk-disp.o
 obj-$(CONFIG_PWM_MXS)          += pwm-mxs.o
 obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o
index 0dbd29e287dbbf495a28fa4b142fb407773b2f0d..172ef82458112c268782d24f55cd882904340ea8 100644 (file)
@@ -339,6 +339,8 @@ int pwmchip_remove(struct pwm_chip *chip)
        unsigned int i;
        int ret = 0;
 
+       pwmchip_sysfs_unexport_children(chip);
+
        mutex_lock(&pwm_lock);
 
        for (i = 0; i < chip->npwm; i++) {
index 65108129d50597eff28c7eb10de9e2d0ac477f20..01339c152ab00bb69b4ff1ee0c87c096151bd91c 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
+#include <linux/slab.h>
 
 #define BERLIN_PWM_EN                  0x0
 #define  BERLIN_PWM_ENABLE             BIT(0)
 #define BERLIN_PWM_TCNT                        0xc
 #define  BERLIN_PWM_MAX_TCNT           65535
 
+struct berlin_pwm_channel {
+       u32 enable;
+       u32 ctrl;
+       u32 duty;
+       u32 tcnt;
+};
+
 struct berlin_pwm_chip {
        struct pwm_chip chip;
        struct clk *clk;
@@ -55,6 +63,25 @@ static inline void berlin_pwm_writel(struct berlin_pwm_chip *chip,
        writel_relaxed(value, chip->base + channel * 0x10 + offset);
 }
 
+static int berlin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct berlin_pwm_channel *channel;
+
+       channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+       if (!channel)
+               return -ENOMEM;
+
+       return pwm_set_chip_data(pwm, channel);
+}
+
+static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct berlin_pwm_channel *channel = pwm_get_chip_data(pwm);
+
+       pwm_set_chip_data(pwm, NULL);
+       kfree(channel);
+}
+
 static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev,
                             int duty_ns, int period_ns)
 {
@@ -137,6 +164,8 @@ static void berlin_pwm_disable(struct pwm_chip *chip,
 }
 
 static const struct pwm_ops berlin_pwm_ops = {
+       .request = berlin_pwm_request,
+       .free = berlin_pwm_free,
        .config = berlin_pwm_config,
        .set_polarity = berlin_pwm_set_polarity,
        .enable = berlin_pwm_enable,
@@ -204,12 +233,67 @@ static int berlin_pwm_remove(struct platform_device *pdev)
        return ret;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int berlin_pwm_suspend(struct device *dev)
+{
+       struct berlin_pwm_chip *pwm = dev_get_drvdata(dev);
+       unsigned int i;
+
+       for (i = 0; i < pwm->chip.npwm; i++) {
+               struct berlin_pwm_channel *channel;
+
+               channel = pwm_get_chip_data(&pwm->chip.pwms[i]);
+               if (!channel)
+                       continue;
+
+               channel->enable = berlin_pwm_readl(pwm, i, BERLIN_PWM_ENABLE);
+               channel->ctrl = berlin_pwm_readl(pwm, i, BERLIN_PWM_CONTROL);
+               channel->duty = berlin_pwm_readl(pwm, i, BERLIN_PWM_DUTY);
+               channel->tcnt = berlin_pwm_readl(pwm, i, BERLIN_PWM_TCNT);
+       }
+
+       clk_disable_unprepare(pwm->clk);
+
+       return 0;
+}
+
+static int berlin_pwm_resume(struct device *dev)
+{
+       struct berlin_pwm_chip *pwm = dev_get_drvdata(dev);
+       unsigned int i;
+       int ret;
+
+       ret = clk_prepare_enable(pwm->clk);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < pwm->chip.npwm; i++) {
+               struct berlin_pwm_channel *channel;
+
+               channel = pwm_get_chip_data(&pwm->chip.pwms[i]);
+               if (!channel)
+                       continue;
+
+               berlin_pwm_writel(pwm, i, channel->ctrl, BERLIN_PWM_CONTROL);
+               berlin_pwm_writel(pwm, i, channel->duty, BERLIN_PWM_DUTY);
+               berlin_pwm_writel(pwm, i, channel->tcnt, BERLIN_PWM_TCNT);
+               berlin_pwm_writel(pwm, i, channel->enable, BERLIN_PWM_ENABLE);
+       }
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(berlin_pwm_pm_ops, berlin_pwm_suspend,
+                        berlin_pwm_resume);
+
 static struct platform_driver berlin_pwm_driver = {
        .probe = berlin_pwm_probe,
        .remove = berlin_pwm_remove,
        .driver = {
                .name = "berlin-pwm",
                .of_match_table = berlin_pwm_match,
+               .pm = &berlin_pwm_pm_ops,
        },
 };
 module_platform_driver(berlin_pwm_driver);
index 99b9acc1a420801a8d6fb6999d45ca97399d30b6..f6ca4e8c6253f0ea8ccb9770045dcc16a142db10 100644 (file)
@@ -38,7 +38,7 @@ static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty)
        struct {
                struct cros_ec_command msg;
                struct ec_params_pwm_set_duty params;
-       } buf;
+       } __packed buf;
        struct ec_params_pwm_set_duty *params = &buf.params;
        struct cros_ec_command *msg = &buf.msg;
 
@@ -65,7 +65,7 @@ static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index,
                        struct ec_params_pwm_get_duty params;
                        struct ec_response_pwm_get_duty resp;
                };
-       } buf;
+       } __packed buf;
        struct ec_params_pwm_get_duty *params = &buf.params;
        struct ec_response_pwm_get_duty *resp = &buf.resp;
        struct cros_ec_command *msg = &buf.msg;
index 19dc64cab2f0f9c88b6aa15275a63b936bdf34c8..d7f5f7de030ddd1f5fe93db7f811bf74d7ff9a08 100644 (file)
@@ -413,14 +413,18 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev)
        }
 
        for (i = 0; i < lpc18xx_pwm->chip.npwm; i++) {
+               struct lpc18xx_pwm_data *data;
+
                pwm = &lpc18xx_pwm->chip.pwms[i];
-               pwm->chip_data = devm_kzalloc(lpc18xx_pwm->dev,
-                                             sizeof(struct lpc18xx_pwm_data),
-                                             GFP_KERNEL);
-               if (!pwm->chip_data) {
+
+               data = devm_kzalloc(lpc18xx_pwm->dev, sizeof(*data),
+                                   GFP_KERNEL);
+               if (!data) {
                        ret = -ENOMEM;
                        goto remove_pwmchip;
                }
+
+               pwm_set_chip_data(pwm, data);
        }
 
        platform_set_drvdata(pdev, lpc18xx_pwm);
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c
new file mode 100644 (file)
index 0000000..381871b
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * 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) 2016 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2014 Amlogic, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * BSD LICENSE
+ *
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2014 Amlogic, Inc.
+ *
+ * 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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define REG_PWM_A              0x0
+#define REG_PWM_B              0x4
+#define PWM_HIGH_SHIFT         16
+
+#define REG_MISC_AB            0x8
+#define MISC_B_CLK_EN          BIT(23)
+#define MISC_A_CLK_EN          BIT(15)
+#define MISC_CLK_DIV_MASK      0x7f
+#define MISC_B_CLK_DIV_SHIFT   16
+#define MISC_A_CLK_DIV_SHIFT   8
+#define MISC_B_CLK_SEL_SHIFT   6
+#define MISC_A_CLK_SEL_SHIFT   4
+#define MISC_CLK_SEL_WIDTH     2
+#define MISC_B_EN              BIT(1)
+#define MISC_A_EN              BIT(0)
+
+static const unsigned int mux_reg_shifts[] = {
+       MISC_A_CLK_SEL_SHIFT,
+       MISC_B_CLK_SEL_SHIFT
+};
+
+struct meson_pwm_channel {
+       unsigned int hi;
+       unsigned int lo;
+       u8 pre_div;
+
+       struct pwm_state state;
+
+       struct clk *clk_parent;
+       struct clk_mux mux;
+       struct clk *clk;
+};
+
+struct meson_pwm_data {
+       const char * const *parent_names;
+};
+
+struct meson_pwm {
+       struct pwm_chip chip;
+       const struct meson_pwm_data *data;
+       void __iomem *base;
+       u8 inverter_mask;
+       spinlock_t lock;
+};
+
+static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip)
+{
+       return container_of(chip, struct meson_pwm, chip);
+}
+
+static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
+       struct device *dev = chip->dev;
+       int err;
+
+       if (!channel)
+               return -ENODEV;
+
+       if (channel->clk_parent) {
+               err = clk_set_parent(channel->clk, channel->clk_parent);
+               if (err < 0) {
+                       dev_err(dev, "failed to set parent %s for %s: %d\n",
+                               __clk_get_name(channel->clk_parent),
+                               __clk_get_name(channel->clk), err);
+                               return err;
+               }
+       }
+
+       err = clk_prepare_enable(channel->clk);
+       if (err < 0) {
+               dev_err(dev, "failed to enable clock %s: %d\n",
+                       __clk_get_name(channel->clk), err);
+               return err;
+       }
+
+       chip->ops->get_state(chip, pwm, &channel->state);
+
+       return 0;
+}
+
+static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
+
+       if (channel)
+               clk_disable_unprepare(channel->clk);
+}
+
+static int meson_pwm_calc(struct meson_pwm *meson,
+                         struct meson_pwm_channel *channel, unsigned int id,
+                         unsigned int duty, unsigned int period)
+{
+       unsigned int pre_div, cnt, duty_cnt;
+       unsigned long fin_freq = -1, fin_ns;
+
+       if (~(meson->inverter_mask >> id) & 0x1)
+               duty = period - duty;
+
+       if (period == channel->state.period &&
+           duty == channel->state.duty_cycle)
+               return 0;
+
+       fin_freq = clk_get_rate(channel->clk);
+       if (fin_freq == 0) {
+               dev_err(meson->chip.dev, "invalid source clock frequency\n");
+               return -EINVAL;
+       }
+
+       dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq);
+       fin_ns = NSEC_PER_SEC / fin_freq;
+
+       /* Calc pre_div with the period */
+       for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) {
+               cnt = DIV_ROUND_CLOSEST(period, fin_ns * (pre_div + 1));
+               dev_dbg(meson->chip.dev, "fin_ns=%lu pre_div=%u cnt=%u\n",
+                       fin_ns, pre_div, cnt);
+               if (cnt <= 0xffff)
+                       break;
+       }
+
+       if (pre_div == MISC_CLK_DIV_MASK) {
+               dev_err(meson->chip.dev, "unable to get period pre_div\n");
+               return -EINVAL;
+       }
+
+       dev_dbg(meson->chip.dev, "period=%u pre_div=%u cnt=%u\n", period,
+               pre_div, cnt);
+
+       if (duty == period) {
+               channel->pre_div = pre_div;
+               channel->hi = cnt;
+               channel->lo = 0;
+       } else if (duty == 0) {
+               channel->pre_div = pre_div;
+               channel->hi = 0;
+               channel->lo = cnt;
+       } else {
+               /* Then check is we can have the duty with the same pre_div */
+               duty_cnt = DIV_ROUND_CLOSEST(duty, fin_ns * (pre_div + 1));
+               if (duty_cnt > 0xffff) {
+                       dev_err(meson->chip.dev, "unable to get duty cycle\n");
+                       return -EINVAL;
+               }
+
+               dev_dbg(meson->chip.dev, "duty=%u pre_div=%u duty_cnt=%u\n",
+                       duty, pre_div, duty_cnt);
+
+               channel->pre_div = pre_div;
+               channel->hi = duty_cnt;
+               channel->lo = cnt - duty_cnt;
+       }
+
+       return 0;
+}
+
+static void meson_pwm_enable(struct meson_pwm *meson,
+                            struct meson_pwm_channel *channel,
+                            unsigned int id)
+{
+       u32 value, clk_shift, clk_enable, enable;
+       unsigned int offset;
+
+       switch (id) {
+       case 0:
+               clk_shift = MISC_A_CLK_DIV_SHIFT;
+               clk_enable = MISC_A_CLK_EN;
+               enable = MISC_A_EN;
+               offset = REG_PWM_A;
+               break;
+
+       case 1:
+               clk_shift = MISC_B_CLK_DIV_SHIFT;
+               clk_enable = MISC_B_CLK_EN;
+               enable = MISC_B_EN;
+               offset = REG_PWM_B;
+               break;
+
+       default:
+               return;
+       }
+
+       value = readl(meson->base + REG_MISC_AB);
+       value &= ~(MISC_CLK_DIV_MASK << clk_shift);
+       value |= channel->pre_div << clk_shift;
+       value |= clk_enable;
+       writel(value, meson->base + REG_MISC_AB);
+
+       value = (channel->hi << PWM_HIGH_SHIFT) | channel->lo;
+       writel(value, meson->base + offset);
+
+       value = readl(meson->base + REG_MISC_AB);
+       value |= enable;
+       writel(value, meson->base + REG_MISC_AB);
+}
+
+static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id)
+{
+       u32 value, enable;
+
+       switch (id) {
+       case 0:
+               enable = MISC_A_EN;
+               break;
+
+       case 1:
+               enable = MISC_B_EN;
+               break;
+
+       default:
+               return;
+       }
+
+       value = readl(meson->base + REG_MISC_AB);
+       value &= ~enable;
+       writel(value, meson->base + REG_MISC_AB);
+}
+
+static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+                          struct pwm_state *state)
+{
+       struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
+       struct meson_pwm *meson = to_meson_pwm(chip);
+       unsigned long flags;
+       int err = 0;
+
+       if (!state)
+               return -EINVAL;
+
+       spin_lock_irqsave(&meson->lock, flags);
+
+       if (!state->enabled) {
+               meson_pwm_disable(meson, pwm->hwpwm);
+               channel->state.enabled = false;
+
+               goto unlock;
+       }
+
+       if (state->period != channel->state.period ||
+           state->duty_cycle != channel->state.duty_cycle ||
+           state->polarity != channel->state.polarity) {
+               if (channel->state.enabled) {
+                       meson_pwm_disable(meson, pwm->hwpwm);
+                       channel->state.enabled = false;
+               }
+
+               if (state->polarity != channel->state.polarity) {
+                       if (state->polarity == PWM_POLARITY_NORMAL)
+                               meson->inverter_mask |= BIT(pwm->hwpwm);
+                       else
+                               meson->inverter_mask &= ~BIT(pwm->hwpwm);
+               }
+
+               err = meson_pwm_calc(meson, channel, pwm->hwpwm,
+                                    state->duty_cycle, state->period);
+               if (err < 0)
+                       goto unlock;
+
+               channel->state.polarity = state->polarity;
+               channel->state.period = state->period;
+               channel->state.duty_cycle = state->duty_cycle;
+       }
+
+       if (state->enabled && !channel->state.enabled) {
+               meson_pwm_enable(meson, channel, pwm->hwpwm);
+               channel->state.enabled = true;
+       }
+
+unlock:
+       spin_unlock_irqrestore(&meson->lock, flags);
+       return err;
+}
+
+static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+                               struct pwm_state *state)
+{
+       struct meson_pwm *meson = to_meson_pwm(chip);
+       u32 value, mask;
+
+       if (!state)
+               return;
+
+       switch (pwm->hwpwm) {
+       case 0:
+               mask = MISC_A_EN;
+               break;
+
+       case 1:
+               mask = MISC_B_EN;
+               break;
+
+       default:
+               return;
+       }
+
+       value = readl(meson->base + REG_MISC_AB);
+       state->enabled = (value & mask) != 0;
+}
+
+static const struct pwm_ops meson_pwm_ops = {
+       .request = meson_pwm_request,
+       .free = meson_pwm_free,
+       .apply = meson_pwm_apply,
+       .get_state = meson_pwm_get_state,
+       .owner = THIS_MODULE,
+};
+
+static const char * const pwm_meson8b_parent_names[] = {
+       "xtal", "vid_pll", "fclk_div4", "fclk_div3"
+};
+
+static const struct meson_pwm_data pwm_meson8b_data = {
+       .parent_names = pwm_meson8b_parent_names,
+};
+
+static const char * const pwm_gxbb_parent_names[] = {
+       "xtal", "hdmi_pll", "fclk_div4", "fclk_div3"
+};
+
+static const struct meson_pwm_data pwm_gxbb_data = {
+       .parent_names = pwm_gxbb_parent_names,
+};
+
+static const struct of_device_id meson_pwm_matches[] = {
+       { .compatible = "amlogic,meson8b-pwm", .data = &pwm_meson8b_data },
+       { .compatible = "amlogic,meson-gxbb-pwm", .data = &pwm_gxbb_data },
+       {},
+};
+MODULE_DEVICE_TABLE(of, meson_pwm_matches);
+
+static int meson_pwm_init_channels(struct meson_pwm *meson,
+                                  struct meson_pwm_channel *channels)
+{
+       struct device *dev = meson->chip.dev;
+       struct device_node *np = dev->of_node;
+       struct clk_init_data init;
+       unsigned int i;
+       char name[255];
+       int err;
+
+       for (i = 0; i < meson->chip.npwm; i++) {
+               struct meson_pwm_channel *channel = &channels[i];
+
+               snprintf(name, sizeof(name), "%s#mux%u", np->full_name, i);
+
+               init.name = name;
+               init.ops = &clk_mux_ops;
+               init.flags = CLK_IS_BASIC;
+               init.parent_names = meson->data->parent_names;
+               init.num_parents = 1 << MISC_CLK_SEL_WIDTH;
+
+               channel->mux.reg = meson->base + REG_MISC_AB;
+               channel->mux.shift = mux_reg_shifts[i];
+               channel->mux.mask = BIT(MISC_CLK_SEL_WIDTH) - 1;
+               channel->mux.flags = 0;
+               channel->mux.lock = &meson->lock;
+               channel->mux.table = NULL;
+               channel->mux.hw.init = &init;
+
+               channel->clk = devm_clk_register(dev, &channel->mux.hw);
+               if (IS_ERR(channel->clk)) {
+                       err = PTR_ERR(channel->clk);
+                       dev_err(dev, "failed to register %s: %d\n", name, err);
+                       return err;
+               }
+
+               snprintf(name, sizeof(name), "clkin%u", i);
+
+               channel->clk_parent = devm_clk_get(dev, name);
+               if (IS_ERR(channel->clk_parent)) {
+                       err = PTR_ERR(channel->clk_parent);
+                       if (err == -EPROBE_DEFER)
+                               return err;
+
+                       channel->clk_parent = NULL;
+               }
+       }
+
+       return 0;
+}
+
+static void meson_pwm_add_channels(struct meson_pwm *meson,
+                                  struct meson_pwm_channel *channels)
+{
+       unsigned int i;
+
+       for (i = 0; i < meson->chip.npwm; i++)
+               pwm_set_chip_data(&meson->chip.pwms[i], &channels[i]);
+}
+
+static int meson_pwm_probe(struct platform_device *pdev)
+{
+       struct meson_pwm_channel *channels;
+       struct meson_pwm *meson;
+       struct resource *regs;
+       int err;
+
+       meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL);
+       if (!meson)
+               return -ENOMEM;
+
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       meson->base = devm_ioremap_resource(&pdev->dev, regs);
+       if (IS_ERR(meson->base))
+               return PTR_ERR(meson->base);
+
+       meson->chip.dev = &pdev->dev;
+       meson->chip.ops = &meson_pwm_ops;
+       meson->chip.base = -1;
+       meson->chip.npwm = 2;
+       meson->chip.of_xlate = of_pwm_xlate_with_flags;
+       meson->chip.of_pwm_n_cells = 3;
+
+       meson->data = of_device_get_match_data(&pdev->dev);
+       meson->inverter_mask = BIT(meson->chip.npwm) - 1;
+
+       channels = devm_kcalloc(&pdev->dev, meson->chip.npwm, sizeof(*meson),
+                               GFP_KERNEL);
+       if (!channels)
+               return -ENOMEM;
+
+       err = meson_pwm_init_channels(meson, channels);
+       if (err < 0)
+               return err;
+
+       err = pwmchip_add(&meson->chip);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err);
+               return err;
+       }
+
+       meson_pwm_add_channels(meson, channels);
+
+       platform_set_drvdata(pdev, meson);
+
+       return 0;
+}
+
+static int meson_pwm_remove(struct platform_device *pdev)
+{
+       struct meson_pwm *meson = platform_get_drvdata(pdev);
+
+       return pwmchip_remove(&meson->chip);
+}
+
+static struct platform_driver meson_pwm_driver = {
+       .driver = {
+               .name = "meson-pwm",
+               .of_match_table = meson_pwm_matches,
+       },
+       .probe = meson_pwm_probe,
+       .remove = meson_pwm_remove,
+};
+module_platform_driver(meson_pwm_driver);
+
+MODULE_ALIAS("platform:meson-pwm");
+MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("Dual BSD/GPL");
index 0ad3385298c00e4d9798718d263a4d9c3f0ced04..893940d45f0d0859d5aaeaa6cd70ff67bb5dd34b 100644 (file)
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/slab.h>
 
 #define DISP_PWM_EN            0x00
-#define PWM_ENABLE_MASK                BIT(0)
 
-#define DISP_PWM_COMMIT                0x08
-#define PWM_COMMIT_MASK                BIT(0)
-
-#define DISP_PWM_CON_0         0x10
 #define PWM_CLKDIV_SHIFT       16
 #define PWM_CLKDIV_MAX         0x3ff
 #define PWM_CLKDIV_MASK                (PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT)
 
-#define DISP_PWM_CON_1         0x14
 #define PWM_PERIOD_BIT_WIDTH   12
 #define PWM_PERIOD_MASK                ((1 << PWM_PERIOD_BIT_WIDTH) - 1)
 
 #define PWM_HIGH_WIDTH_SHIFT   16
 #define PWM_HIGH_WIDTH_MASK    (0x1fff << PWM_HIGH_WIDTH_SHIFT)
 
+struct mtk_pwm_data {
+       u32 enable_mask;
+       unsigned int con0;
+       u32 con0_sel;
+       unsigned int con1;
+
+       bool has_commit;
+       unsigned int commit;
+       unsigned int commit_mask;
+
+       unsigned int bls_debug;
+       u32 bls_debug_mask;
+};
+
 struct mtk_disp_pwm {
        struct pwm_chip chip;
+       const struct mtk_pwm_data *data;
        struct clk *clk_main;
        struct clk *clk_mm;
        void __iomem *base;
@@ -106,12 +116,21 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                return err;
        }
 
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_0, PWM_CLKDIV_MASK,
+       mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
+                                PWM_CLKDIV_MASK,
                                 clk_div << PWM_CLKDIV_SHIFT);
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_1,
-                                PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK, value);
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 1);
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 0);
+       mtk_disp_pwm_update_bits(mdp, mdp->data->con1,
+                                PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK,
+                                value);
+
+       if (mdp->data->has_commit) {
+               mtk_disp_pwm_update_bits(mdp, mdp->data->commit,
+                                        mdp->data->commit_mask,
+                                        mdp->data->commit_mask);
+               mtk_disp_pwm_update_bits(mdp, mdp->data->commit,
+                                        mdp->data->commit_mask,
+                                        0x0);
+       }
 
        clk_disable(mdp->clk_mm);
        clk_disable(mdp->clk_main);
@@ -134,7 +153,8 @@ static int mtk_disp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
                return err;
        }
 
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 1);
+       mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask,
+                                mdp->data->enable_mask);
 
        return 0;
 }
@@ -143,7 +163,8 @@ static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
        struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
 
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 0);
+       mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask,
+                                0x0);
 
        clk_disable(mdp->clk_mm);
        clk_disable(mdp->clk_main);
@@ -166,6 +187,8 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
        if (!mdp)
                return -ENOMEM;
 
+       mdp->data = of_device_get_match_data(&pdev->dev);
+
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        mdp->base = devm_ioremap_resource(&pdev->dev, r);
        if (IS_ERR(mdp->base))
@@ -200,6 +223,19 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, mdp);
 
+       /*
+        * For MT2701, disable double buffer before writing register
+        * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH.
+        */
+       if (!mdp->data->has_commit) {
+               mtk_disp_pwm_update_bits(mdp, mdp->data->bls_debug,
+                                        mdp->data->bls_debug_mask,
+                                        mdp->data->bls_debug_mask);
+               mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
+                                        mdp->data->con0_sel,
+                                        mdp->data->con0_sel);
+       }
+
        return 0;
 
 disable_clk_mm:
@@ -221,9 +257,30 @@ static int mtk_disp_pwm_remove(struct platform_device *pdev)
        return ret;
 }
 
+static const struct mtk_pwm_data mt2701_pwm_data = {
+       .enable_mask = BIT(16),
+       .con0 = 0xa8,
+       .con0_sel = 0x2,
+       .con1 = 0xac,
+       .has_commit = false,
+       .bls_debug = 0xb0,
+       .bls_debug_mask = 0x3,
+};
+
+static const struct mtk_pwm_data mt8173_pwm_data = {
+       .enable_mask = BIT(0),
+       .con0 = 0x10,
+       .con0_sel = 0x0,
+       .con1 = 0x14,
+       .has_commit = true,
+       .commit = 0x8,
+       .commit_mask = 0x1,
+};
+
 static const struct of_device_id mtk_disp_pwm_of_match[] = {
-       { .compatible = "mediatek,mt8173-disp-pwm" },
-       { .compatible = "mediatek,mt6595-disp-pwm" },
+       { .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data},
+       { .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data},
+       { .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data},
        { }
 };
 MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
index ada2d326dc3e6117f9543f5283ac09c90bcb22ad..f113cda47032e8c48f5ee9791d306af3b8d4e809 100644 (file)
@@ -193,9 +193,18 @@ static unsigned long pwm_samsung_calc_tin(struct samsung_pwm_chip *chip,
         * divider settings and choose the lowest divisor that can generate
         * frequencies lower than requested.
         */
-       for (div = variant->div_base; div < 4; ++div)
-               if ((rate >> (variant->bits + div)) < freq)
-                       break;
+       if (variant->bits < 32) {
+               /* Only for s3c24xx */
+               for (div = variant->div_base; div < 4; ++div)
+                       if ((rate >> (variant->bits + div)) < freq)
+                               break;
+       } else {
+               /*
+                * Other variants have enough counter bits to generate any
+                * requested rate, so no need to check higher divisors.
+                */
+               div = variant->div_base;
+       }
 
        pwm_samsung_set_divisor(chip, chan, BIT(div));
 
index 92abbd56b9f7183810eb222b8b194f3817838367..dd82dc840af98b3b4a039b60054b666d5de8d5b6 100644 (file)
@@ -1,8 +1,10 @@
 /*
- * PWM device driver for ST SoCs.
- * Author: Ajit Pal Singh <ajitpal.singh@st.com>
+ * PWM device driver for ST SoCs
+ *
+ * Copyright (C) 2013-2016 STMicroelectronics (R&D) Limited
  *
- * Copyright (C) 2013-2014 STMicroelectronics (R&D) Limited
+ * Author: Ajit Pal Singh <ajitpal.singh@st.com>
+ *         Lee Jones <lee.jones@linaro.org>
  *
  * 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
@@ -11,6 +13,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/interrupt.h>
 #include <linux/math64.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/regmap.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/time.h>
+#include <linux/wait.h>
+
+#define PWM_OUT_VAL(x) (0x00 + (4 * (x))) /* Device's Duty Cycle register */
+#define PWM_CPT_VAL(x) (0x10 + (4 * (x))) /* Capture value */
+#define PWM_CPT_EDGE(x) (0x30 + (4 * (x))) /* Edge to capture on */
 
-#define STI_DS_REG(ch) (4 * (ch))      /* Channel's Duty Cycle register */
-#define STI_PWMCR      0x50            /* Control/Config register */
-#define STI_INTEN      0x54            /* Interrupt Enable/Disable register */
-#define PWM_PRESCALE_LOW_MASK          0x0f
-#define PWM_PRESCALE_HIGH_MASK         0xf0
+#define STI_PWM_CTRL           0x50    /* Control/Config register */
+#define STI_INT_EN             0x54    /* Interrupt Enable/Disable register */
+#define STI_INT_STA            0x58    /* Interrupt Status register */
+#define PWM_INT_ACK            0x5c
+#define PWM_PRESCALE_LOW_MASK  0x0f
+#define PWM_PRESCALE_HIGH_MASK 0xf0
+#define PWM_CPT_EDGE_MASK      0x03
+#define PWM_INT_ACK_MASK       0x1ff
+
+#define STI_MAX_CPT_DEVS       4
+#define CPT_DC_MAX             0xff
 
 /* Regfield IDs */
 enum {
+       /* Bits in PWM_CTRL*/
        PWMCLK_PRESCALE_LOW,
        PWMCLK_PRESCALE_HIGH,
-       PWM_EN,
-       PWM_INT_EN,
+       CPTCLK_PRESCALE,
+
+       PWM_OUT_EN,
+       PWM_CPT_EN,
+
+       PWM_CPT_INT_EN,
+       PWM_CPT_INT_STAT,
 
        /* Keep last */
        MAX_REGFIELDS
 };
 
+/*
+ * Each capture input can be programmed to detect rising-edge, falling-edge,
+ * either edge or neither egde.
+ */
+enum sti_cpt_edge {
+       CPT_EDGE_DISABLED,
+       CPT_EDGE_RISING,
+       CPT_EDGE_FALLING,
+       CPT_EDGE_BOTH,
+};
+
+struct sti_cpt_ddata {
+       u32 snapshot[3];
+       unsigned int index;
+       struct mutex lock;
+       wait_queue_head_t wait;
+};
+
 struct sti_pwm_compat_data {
        const struct reg_field *reg_fields;
-       unsigned int num_chan;
+       unsigned int pwm_num_devs;
+       unsigned int cpt_num_devs;
        unsigned int max_pwm_cnt;
        unsigned int max_prescale;
 };
 
 struct sti_pwm_chip {
        struct device *dev;
-       struct clk *clk;
-       unsigned long clk_rate;
+       struct clk *pwm_clk;
+       struct clk *cpt_clk;
        struct regmap *regmap;
        struct sti_pwm_compat_data *cdata;
        struct regmap_field *prescale_low;
        struct regmap_field *prescale_high;
-       struct regmap_field *pwm_en;
-       struct regmap_field *pwm_int_en;
+       struct regmap_field *pwm_out_en;
+       struct regmap_field *pwm_cpt_en;
+       struct regmap_field *pwm_cpt_int_en;
+       struct regmap_field *pwm_cpt_int_stat;
        struct pwm_chip chip;
        struct pwm_device *cur;
        unsigned long configured;
@@ -64,10 +106,13 @@ struct sti_pwm_chip {
 };
 
 static const struct reg_field sti_pwm_regfields[MAX_REGFIELDS] = {
-       [PWMCLK_PRESCALE_LOW]   = REG_FIELD(STI_PWMCR, 0, 3),
-       [PWMCLK_PRESCALE_HIGH]  = REG_FIELD(STI_PWMCR, 11, 14),
-       [PWM_EN]                = REG_FIELD(STI_PWMCR, 9, 9),
-       [PWM_INT_EN]            = REG_FIELD(STI_INTEN, 0, 0),
+       [PWMCLK_PRESCALE_LOW] = REG_FIELD(STI_PWM_CTRL, 0, 3),
+       [PWMCLK_PRESCALE_HIGH] = REG_FIELD(STI_PWM_CTRL, 11, 14),
+       [CPTCLK_PRESCALE] = REG_FIELD(STI_PWM_CTRL, 4, 8),
+       [PWM_OUT_EN] = REG_FIELD(STI_PWM_CTRL, 9, 9),
+       [PWM_CPT_EN] = REG_FIELD(STI_PWM_CTRL, 10, 10),
+       [PWM_CPT_INT_EN] = REG_FIELD(STI_INT_EN, 1, 4),
+       [PWM_CPT_INT_STAT] = REG_FIELD(STI_INT_STA, 1, 4),
 };
 
 static inline struct sti_pwm_chip *to_sti_pwmchip(struct pwm_chip *chip)
@@ -82,61 +127,68 @@ static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period,
                                unsigned int *prescale)
 {
        struct sti_pwm_compat_data *cdata = pc->cdata;
-       unsigned long val;
+       unsigned long clk_rate;
+       unsigned long value;
        unsigned int ps;
 
+       clk_rate = clk_get_rate(pc->pwm_clk);
+       if (!clk_rate) {
+               dev_err(pc->dev, "failed to get clock rate\n");
+               return -EINVAL;
+       }
+
        /*
-        * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_count + 1)) - 1
+        * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_cnt + 1)) - 1
         */
-       val = NSEC_PER_SEC / pc->clk_rate;
-       val *= cdata->max_pwm_cnt + 1;
+       value = NSEC_PER_SEC / clk_rate;
+       value *= cdata->max_pwm_cnt + 1;
 
-       if (period % val) {
+       if (period % value)
                return -EINVAL;
-       } else {
-               ps  = period / val - 1;
-               if (ps > cdata->max_prescale)
-                       return -EINVAL;
-       }
+
+       ps  = period / value - 1;
+       if (ps > cdata->max_prescale)
+               return -EINVAL;
+
        *prescale = ps;
 
        return 0;
 }
 
 /*
- * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles.
- * The only way to change the period (apart from changing the PWM input clock)
- * is to change the PWM clock prescaler.
- * The prescaler is of 8 bits, so 256 prescaler values and hence
- * 256 possible period values are supported (for a particular clock rate).
- * The requested period will be applied only if it matches one of these
- * 256 values.
+ * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles. The
+ * only way to change the period (apart from changing the PWM input clock) is
+ * to change the PWM clock prescaler.
+ *
+ * The prescaler is of 8 bits, so 256 prescaler values and hence 256 possible
+ * period values are supported (for a particular clock rate). The requested
+ * period will be applied only if it matches one of these 256 values.
  */
 static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-                        int duty_ns, int period_ns)
+                         int duty_ns, int period_ns)
 {
        struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
        struct sti_pwm_compat_data *cdata = pc->cdata;
+       unsigned int ncfg, value, prescale = 0;
        struct pwm_device *cur = pc->cur;
        struct device *dev = pc->dev;
-       unsigned int prescale = 0, pwmvalx;
-       int ret;
-       unsigned int ncfg;
        bool period_same = false;
+       int ret;
 
        ncfg = hweight_long(pc->configured);
        if (ncfg)
                period_same = (period_ns == pwm_get_period(cur));
 
-       /* Allow configuration changes if one of the
-        * following conditions satisfy.
-        * 1. No channels have been configured.
-        * 2. Only one channel has been configured and the new request
-        *    is for the same channel.
-        * 3. Only one channel has been configured and the new request is
-        *    for a new channel and period of the new channel is same as
-        *    the current configured period.
-        * 4. More than one channels are configured and period of the new
+       /*
+        * Allow configuration changes if one of the following conditions
+        * satisfy.
+        * 1. No devices have been configured.
+        * 2. Only one device has been configured and the new request is for
+        *    the same device.
+        * 3. Only one device has been configured and the new request is for
+        *    a new device and period of the new device is same as the current
+        *    configured period.
+        * 4. More than one devices are configured and period of the new
         *    requestis the same as the current period.
         */
        if (!ncfg ||
@@ -144,7 +196,11 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
            ((ncfg == 1) && (pwm->hwpwm != cur->hwpwm) && period_same) ||
            ((ncfg > 1) && period_same)) {
                /* Enable clock before writing to PWM registers. */
-               ret = clk_enable(pc->clk);
+               ret = clk_enable(pc->pwm_clk);
+               if (ret)
+                       return ret;
+
+               ret = clk_enable(pc->cpt_clk);
                if (ret)
                        return ret;
 
@@ -153,15 +209,15 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                        if (ret)
                                goto clk_dis;
 
-                       ret =
-                       regmap_field_write(pc->prescale_low,
-                                          prescale & PWM_PRESCALE_LOW_MASK);
+                       value = prescale & PWM_PRESCALE_LOW_MASK;
+
+                       ret = regmap_field_write(pc->prescale_low, value);
                        if (ret)
                                goto clk_dis;
 
-                       ret =
-                       regmap_field_write(pc->prescale_high,
-                               (prescale & PWM_PRESCALE_HIGH_MASK) >> 4);
+                       value = (prescale & PWM_PRESCALE_HIGH_MASK) >> 4;
+
+                       ret = regmap_field_write(pc->prescale_high, value);
                        if (ret)
                                goto clk_dis;
                }
@@ -172,25 +228,26 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                 * PWM pulse = (max_pwm_count + 1) local cycles,
                 * that is continuous pulse: signal never goes low.
                 */
-               pwmvalx = cdata->max_pwm_cnt * duty_ns / period_ns;
+               value = cdata->max_pwm_cnt * duty_ns / period_ns;
 
-               ret = regmap_write(pc->regmap, STI_DS_REG(pwm->hwpwm), pwmvalx);
+               ret = regmap_write(pc->regmap, PWM_OUT_VAL(pwm->hwpwm), value);
                if (ret)
                        goto clk_dis;
 
-               ret = regmap_field_write(pc->pwm_int_en, 0);
+               ret = regmap_field_write(pc->pwm_cpt_int_en, 0);
 
                set_bit(pwm->hwpwm, &pc->configured);
                pc->cur = pwm;
 
-               dev_dbg(dev, "prescale:%u, period:%i, duty:%i, pwmvalx:%u\n",
-                       prescale, period_ns, duty_ns, pwmvalx);
+               dev_dbg(dev, "prescale:%u, period:%i, duty:%i, value:%u\n",
+                       prescale, period_ns, duty_ns, value);
        } else {
                return -EINVAL;
        }
 
 clk_dis:
-       clk_disable(pc->clk);
+       clk_disable(pc->pwm_clk);
+       clk_disable(pc->cpt_clk);
        return ret;
 }
 
@@ -201,23 +258,30 @@ static int sti_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
        int ret = 0;
 
        /*
-        * Since we have a common enable for all PWM channels,
-        * do not enable if already enabled.
+        * Since we have a common enable for all PWM devices, do not enable if
+        * already enabled.
         */
        mutex_lock(&pc->sti_pwm_lock);
+
        if (!pc->en_count) {
-               ret = clk_enable(pc->clk);
+               ret = clk_enable(pc->pwm_clk);
+               if (ret)
+                       goto out;
+
+               ret = clk_enable(pc->cpt_clk);
                if (ret)
                        goto out;
 
-               ret = regmap_field_write(pc->pwm_en, 1);
+               ret = regmap_field_write(pc->pwm_out_en, 1);
                if (ret) {
-                       dev_err(dev, "failed to enable PWM device:%d\n",
-                               pwm->hwpwm);
+                       dev_err(dev, "failed to enable PWM device %u: %d\n",
+                               pwm->hwpwm, ret);
                        goto out;
                }
        }
+
        pc->en_count++;
+
 out:
        mutex_unlock(&pc->sti_pwm_lock);
        return ret;
@@ -228,13 +292,17 @@ static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
        struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
 
        mutex_lock(&pc->sti_pwm_lock);
+
        if (--pc->en_count) {
                mutex_unlock(&pc->sti_pwm_lock);
                return;
        }
-       regmap_field_write(pc->pwm_en, 0);
 
-       clk_disable(pc->clk);
+       regmap_field_write(pc->pwm_out_en, 0);
+
+       clk_disable(pc->pwm_clk);
+       clk_disable(pc->cpt_clk);
+
        mutex_unlock(&pc->sti_pwm_lock);
 }
 
@@ -245,7 +313,90 @@ static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
        clear_bit(pwm->hwpwm, &pc->configured);
 }
 
+static int sti_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
+                          struct pwm_capture *result, unsigned long timeout)
+{
+       struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
+       struct sti_pwm_compat_data *cdata = pc->cdata;
+       struct sti_cpt_ddata *ddata = pwm_get_chip_data(pwm);
+       struct device *dev = pc->dev;
+       unsigned int effective_ticks;
+       unsigned long long high, low;
+       int ret;
+
+       if (pwm->hwpwm >= cdata->cpt_num_devs) {
+               dev_err(dev, "device %u is not valid\n", pwm->hwpwm);
+               return -EINVAL;
+       }
+
+       mutex_lock(&ddata->lock);
+       ddata->index = 0;
+
+       /* Prepare capture measurement */
+       regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_RISING);
+       regmap_field_write(pc->pwm_cpt_int_en, BIT(pwm->hwpwm));
+
+       /* Enable capture */
+       ret = regmap_field_write(pc->pwm_cpt_en, 1);
+       if (ret) {
+               dev_err(dev, "failed to enable PWM capture %u: %d\n",
+                       pwm->hwpwm, ret);
+               goto out;
+       }
+
+       ret = wait_event_interruptible_timeout(ddata->wait, ddata->index > 1,
+                                              msecs_to_jiffies(timeout));
+
+       regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_DISABLED);
+
+       if (ret == -ERESTARTSYS)
+               goto out;
+
+       switch (ddata->index) {
+       case 0:
+       case 1:
+               /*
+                * Getting here could mean:
+                *  - input signal is constant of less than 1 Hz
+                *  - there is no input signal at all
+                *
+                * In such case the frequency is rounded down to 0
+                */
+               result->period = 0;
+               result->duty_cycle = 0;
+
+               break;
+
+       case 2:
+               /* We have everying we need */
+               high = ddata->snapshot[1] - ddata->snapshot[0];
+               low = ddata->snapshot[2] - ddata->snapshot[1];
+
+               effective_ticks = clk_get_rate(pc->cpt_clk);
+
+               result->period = (high + low) * NSEC_PER_SEC;
+               result->period /= effective_ticks;
+
+               result->duty_cycle = high * NSEC_PER_SEC;
+               result->duty_cycle /= effective_ticks;
+
+               break;
+
+       default:
+               dev_err(dev, "internal error\n");
+               break;
+       }
+
+out:
+       /* Disable capture */
+       regmap_field_write(pc->pwm_cpt_en, 0);
+
+       mutex_unlock(&ddata->lock);
+       return ret;
+}
+
 static const struct pwm_ops sti_pwm_ops = {
+       .capture = sti_pwm_capture,
        .config = sti_pwm_config,
        .enable = sti_pwm_enable,
        .disable = sti_pwm_disable,
@@ -253,17 +404,98 @@ static const struct pwm_ops sti_pwm_ops = {
        .owner = THIS_MODULE,
 };
 
+static irqreturn_t sti_pwm_interrupt(int irq, void *data)
+{
+       struct sti_pwm_chip *pc = data;
+       struct device *dev = pc->dev;
+       struct sti_cpt_ddata *ddata;
+       int devicenum;
+       unsigned int cpt_int_stat;
+       unsigned int reg;
+       int ret = IRQ_NONE;
+
+       ret = regmap_field_read(pc->pwm_cpt_int_stat, &cpt_int_stat);
+       if (ret)
+               return ret;
+
+       while (cpt_int_stat) {
+               devicenum = ffs(cpt_int_stat) - 1;
+
+               ddata = pwm_get_chip_data(&pc->chip.pwms[devicenum]);
+
+               /*
+                * Capture input:
+                *    _______                   _______
+                *   |       |                 |       |
+                * __|       |_________________|       |________
+                *   ^0      ^1                ^2
+                *
+                * Capture start by the first available rising edge. When a
+                * capture event occurs, capture value (CPT_VALx) is stored,
+                * index incremented, capture edge changed.
+                *
+                * After the capture, if the index > 1, we have collected the
+                * necessary data so we signal the thread waiting for it and
+                * disable the capture by setting capture edge to none
+                */
+
+               regmap_read(pc->regmap,
+                           PWM_CPT_VAL(devicenum),
+                           &ddata->snapshot[ddata->index]);
+
+               switch (ddata->index) {
+               case 0:
+               case 1:
+                       regmap_read(pc->regmap, PWM_CPT_EDGE(devicenum), &reg);
+                       reg ^= PWM_CPT_EDGE_MASK;
+                       regmap_write(pc->regmap, PWM_CPT_EDGE(devicenum), reg);
+
+                       ddata->index++;
+                       break;
+
+               case 2:
+                       regmap_write(pc->regmap,
+                                    PWM_CPT_EDGE(devicenum),
+                                    CPT_EDGE_DISABLED);
+                       wake_up(&ddata->wait);
+                       break;
+
+               default:
+                       dev_err(dev, "Internal error\n");
+               }
+
+               cpt_int_stat &= ~BIT_MASK(devicenum);
+
+               ret = IRQ_HANDLED;
+       }
+
+       /* Just ACK everything */
+       regmap_write(pc->regmap, PWM_INT_ACK, PWM_INT_ACK_MASK);
+
+       return ret;
+}
+
 static int sti_pwm_probe_dt(struct sti_pwm_chip *pc)
 {
        struct device *dev = pc->dev;
        const struct reg_field *reg_fields;
        struct device_node *np = dev->of_node;
        struct sti_pwm_compat_data *cdata = pc->cdata;
-       u32 num_chan;
+       u32 num_devs;
+       int ret;
+
+       ret = of_property_read_u32(np, "st,pwm-num-chan", &num_devs);
+       if (!ret)
+               cdata->pwm_num_devs = num_devs;
+
+       ret = of_property_read_u32(np, "st,capture-num-chan", &num_devs);
+       if (!ret)
+               cdata->cpt_num_devs = num_devs;
 
-       of_property_read_u32(np, "st,pwm-num-chan", &num_chan);
-       if (num_chan)
-               cdata->num_chan = num_chan;
+       if (!cdata->pwm_num_devs && !cdata->cpt_num_devs) {
+               dev_err(dev, "No channels configured\n");
+               return -EINVAL;
+       }
 
        reg_fields = cdata->reg_fields;
 
@@ -277,15 +509,26 @@ static int sti_pwm_probe_dt(struct sti_pwm_chip *pc)
        if (IS_ERR(pc->prescale_high))
                return PTR_ERR(pc->prescale_high);
 
-       pc->pwm_en = devm_regmap_field_alloc(dev, pc->regmap,
-                                            reg_fields[PWM_EN]);
-       if (IS_ERR(pc->pwm_en))
-               return PTR_ERR(pc->pwm_en);
 
-       pc->pwm_int_en = devm_regmap_field_alloc(dev, pc->regmap,
-                                                reg_fields[PWM_INT_EN]);
-       if (IS_ERR(pc->pwm_int_en))
-               return PTR_ERR(pc->pwm_int_en);
+       pc->pwm_out_en = devm_regmap_field_alloc(dev, pc->regmap,
+                                                reg_fields[PWM_OUT_EN]);
+       if (IS_ERR(pc->pwm_out_en))
+               return PTR_ERR(pc->pwm_out_en);
+
+       pc->pwm_cpt_en = devm_regmap_field_alloc(dev, pc->regmap,
+                                                reg_fields[PWM_CPT_EN]);
+       if (IS_ERR(pc->pwm_cpt_en))
+               return PTR_ERR(pc->pwm_cpt_en);
+
+       pc->pwm_cpt_int_en = devm_regmap_field_alloc(dev, pc->regmap,
+                                               reg_fields[PWM_CPT_INT_EN]);
+       if (IS_ERR(pc->pwm_cpt_int_en))
+               return PTR_ERR(pc->pwm_cpt_int_en);
+
+       pc->pwm_cpt_int_stat = devm_regmap_field_alloc(dev, pc->regmap,
+                                               reg_fields[PWM_CPT_INT_STAT]);
+       if (PTR_ERR_OR_ZERO(pc->pwm_cpt_int_stat))
+               return PTR_ERR(pc->pwm_cpt_int_stat);
 
        return 0;
 }
@@ -302,7 +545,8 @@ static int sti_pwm_probe(struct platform_device *pdev)
        struct sti_pwm_compat_data *cdata;
        struct sti_pwm_chip *pc;
        struct resource *res;
-       int ret;
+       unsigned int i;
+       int irq, ret;
 
        pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
        if (!pc)
@@ -323,14 +567,28 @@ static int sti_pwm_probe(struct platform_device *pdev)
        if (IS_ERR(pc->regmap))
                return PTR_ERR(pc->regmap);
 
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "Failed to obtain IRQ\n");
+               return irq;
+       }
+
+       ret = devm_request_irq(&pdev->dev, irq, sti_pwm_interrupt, 0,
+                              pdev->name, pc);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to request IRQ\n");
+               return ret;
+       }
+
        /*
         * Setup PWM data with default values: some values could be replaced
         * with specific ones provided from Device Tree.
         */
-       cdata->reg_fields   = &sti_pwm_regfields[0];
+       cdata->reg_fields = sti_pwm_regfields;
        cdata->max_prescale = 0xff;
-       cdata->max_pwm_cnt  = 255;
-       cdata->num_chan     = 1;
+       cdata->max_pwm_cnt = 255;
+       cdata->pwm_num_devs = 0;
+       cdata->cpt_num_devs = 0;
 
        pc->cdata = cdata;
        pc->dev = dev;
@@ -341,36 +599,64 @@ static int sti_pwm_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       pc->clk = of_clk_get_by_name(dev->of_node, "pwm");
-       if (IS_ERR(pc->clk)) {
+       if (!cdata->pwm_num_devs)
+               goto skip_pwm;
+
+       pc->pwm_clk = of_clk_get_by_name(dev->of_node, "pwm");
+       if (IS_ERR(pc->pwm_clk)) {
                dev_err(dev, "failed to get PWM clock\n");
-               return PTR_ERR(pc->clk);
+               return PTR_ERR(pc->pwm_clk);
        }
 
-       pc->clk_rate = clk_get_rate(pc->clk);
-       if (!pc->clk_rate) {
-               dev_err(dev, "failed to get clock rate\n");
-               return -EINVAL;
+       ret = clk_prepare(pc->pwm_clk);
+       if (ret) {
+               dev_err(dev, "failed to prepare clock\n");
+               return ret;
        }
 
-       ret = clk_prepare(pc->clk);
+skip_pwm:
+       if (!cdata->cpt_num_devs)
+               goto skip_cpt;
+
+       pc->cpt_clk = of_clk_get_by_name(dev->of_node, "capture");
+       if (IS_ERR(pc->cpt_clk)) {
+               dev_err(dev, "failed to get PWM capture clock\n");
+               return PTR_ERR(pc->cpt_clk);
+       }
+
+       ret = clk_prepare(pc->cpt_clk);
        if (ret) {
                dev_err(dev, "failed to prepare clock\n");
                return ret;
        }
 
+skip_cpt:
        pc->chip.dev = dev;
        pc->chip.ops = &sti_pwm_ops;
        pc->chip.base = -1;
-       pc->chip.npwm = pc->cdata->num_chan;
+       pc->chip.npwm = pc->cdata->pwm_num_devs;
        pc->chip.can_sleep = true;
 
        ret = pwmchip_add(&pc->chip);
        if (ret < 0) {
-               clk_unprepare(pc->clk);
+               clk_unprepare(pc->pwm_clk);
+               clk_unprepare(pc->cpt_clk);
                return ret;
        }
 
+       for (i = 0; i < cdata->cpt_num_devs; i++) {
+               struct sti_cpt_ddata *ddata;
+
+               ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+               if (!ddata)
+                       return -ENOMEM;
+
+               init_waitqueue_head(&ddata->wait);
+               mutex_init(&ddata->lock);
+
+               pwm_set_chip_data(&pc->chip.pwms[i], ddata);
+       }
+
        platform_set_drvdata(pdev, pc);
 
        return 0;
@@ -381,10 +667,11 @@ static int sti_pwm_remove(struct platform_device *pdev)
        struct sti_pwm_chip *pc = platform_get_drvdata(pdev);
        unsigned int i;
 
-       for (i = 0; i < pc->cdata->num_chan; i++)
+       for (i = 0; i < pc->cdata->pwm_num_devs; i++)
                pwm_disable(&pc->chip.pwms[i]);
 
-       clk_unprepare(pc->clk);
+       clk_unprepare(pc->pwm_clk);
+       clk_unprepare(pc->cpt_clk);
 
        return pwmchip_remove(&pc->chip);
 }
index 03a99a53c39e2709247ce64f21629c4ac5181b79..b0803f6c64d9200909fdebec0752b2060a436df8 100644 (file)
@@ -284,6 +284,12 @@ static const struct sun4i_pwm_data sun4i_pwm_data_a20 = {
        .npwm = 2,
 };
 
+static const struct sun4i_pwm_data sun4i_pwm_data_h3 = {
+       .has_prescaler_bypass = true,
+       .has_rdy = true,
+       .npwm = 1,
+};
+
 static const struct of_device_id sun4i_pwm_dt_ids[] = {
        {
                .compatible = "allwinner,sun4i-a10-pwm",
@@ -297,6 +303,9 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = {
        }, {
                .compatible = "allwinner,sun7i-a20-pwm",
                .data = &sun4i_pwm_data_a20,
+       }, {
+               .compatible = "allwinner,sun8i-h3-pwm",
+               .data = &sun4i_pwm_data_h3,
        }, {
                /* sentinel */
        },
index 829f4991c96f5e8a5a6c9af4d3eef76dde2f25e2..7fa85a1604da986e63debaaeabb8ad4832976c18 100644 (file)
@@ -34,7 +34,6 @@ static int pwmss_probe(struct platform_device *pdev)
        struct device_node *node = pdev->dev.of_node;
 
        pm_runtime_enable(&pdev->dev);
-       pm_runtime_get_sync(&pdev->dev);
 
        /* Populate all the child nodes here... */
        ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
@@ -46,31 +45,13 @@ static int pwmss_probe(struct platform_device *pdev)
 
 static int pwmss_remove(struct platform_device *pdev)
 {
-       pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int pwmss_suspend(struct device *dev)
-{
-       pm_runtime_put_sync(dev);
-       return 0;
-}
-
-static int pwmss_resume(struct device *dev)
-{
-       pm_runtime_get_sync(dev);
-       return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(pwmss_pm_ops, pwmss_suspend, pwmss_resume);
-
 static struct platform_driver pwmss_driver = {
        .driver = {
                .name   = "pwmss",
-               .pm     = &pwmss_pm_ops,
                .of_match_table = pwmss_of_match,
        },
        .probe  = pwmss_probe,
index 04f76725d59158e1da8fa6968b470edfc0670d36..7a993b0566383c38358bbf17784d30221c051749 100644 (file)
@@ -269,6 +269,22 @@ static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
                goto out;
        }
 
+       val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN);
+
+       ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
+       if (ret < 0) {
+               dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
+               goto out;
+       }
+
+       val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN);
+
+       ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
+       if (ret < 0) {
+               dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
+               goto out;
+       }
+
        twl->twl6030_toggle3 = val;
 out:
        mutex_unlock(&twl->mutex);
index 18ed725594c316ab71edafdafb5e1912714de7f1..0296d8178ae29d509306670c9bb85147c50f0633 100644 (file)
@@ -409,6 +409,24 @@ void pwmchip_sysfs_unexport(struct pwm_chip *chip)
        }
 }
 
+void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
+{
+       struct device *parent;
+       unsigned int i;
+
+       parent = class_find_device(&pwm_class, NULL, chip,
+                                  pwmchip_sysfs_match);
+       if (!parent)
+               return;
+
+       for (i = 0; i < chip->npwm; i++) {
+               struct pwm_device *pwm = &chip->pwms[i];
+
+               if (test_bit(PWMF_EXPORTED, &pwm->flags))
+                       pwm_unexport_child(parent, pwm);
+       }
+}
+
 static int __init pwm_sysfs_init(void)
 {
        return class_register(&pwm_class);
index 436dfe871d3230436adb301ccec42563063dfb0b..9013a585507e8a80b4580c084445ec7bbde6e04c 100644 (file)
@@ -892,7 +892,8 @@ rio_dma_transfer(struct file *filp, u32 transfer_mode,
                down_read(&current->mm->mmap_sem);
                pinned = get_user_pages(
                                (unsigned long)xfer->loc_addr & PAGE_MASK,
-                               nr_pages, dir == DMA_FROM_DEVICE, 0,
+                               nr_pages,
+                               dir == DMA_FROM_DEVICE ? FOLL_WRITE : 0,
                                page_list, NULL);
                up_read(&current->mm->mmap_sem);
 
index 3958f50c5975c4d7bb012016fc2871525efe07ec..e0c747aa9f851d5e394f59348d6b531bf1c7b664 100644 (file)
@@ -495,7 +495,8 @@ static irqreturn_t max8973_thermal_irq(int irq, void *data)
 {
        struct max8973_chip *mchip = data;
 
-       thermal_zone_device_update(mchip->tz_device);
+       thermal_zone_device_update(mchip->tz_device,
+                                  THERMAL_EVENT_UNSPECIFIED);
 
        return IRQ_HANDLED;
 }
index d1e080701264f230235668b07aedf0e4936ba4f9..e859d148aba9ecdbcecc47e745209b94ed83dd67 100644 (file)
@@ -208,14 +208,14 @@ config RTC_DRV_AS3722
          will be called rtc-as3722.
 
 config RTC_DRV_DS1307
-       tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025"
+       tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025, ISL12057"
        help
          If you say yes here you get support for various compatible RTC
          chips (often with battery backup) connected with I2C. This driver
          should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00,
-         EPSON RX-8025 and probably other chips. In some cases the RTC
-         must already have been initialized (by manufacturing or a
-         bootloader).
+         EPSON RX-8025, Intersil ISL12057 and probably other chips. In some
+         cases the RTC must already have been initialized (by manufacturing or
+         bootloader).
 
          The first seven registers on these chips hold an RTC, and other
          registers may add features such as NVRAM, a trickle charger for
@@ -234,6 +234,20 @@ config RTC_DRV_DS1307_HWMON
          Say Y here if you want to expose temperature sensor data on
          rtc-ds1307 (only DS3231)
 
+config RTC_DRV_DS1307_CENTURY
+       bool "Century bit support for rtc-ds1307"
+       depends on RTC_DRV_DS1307
+       default n
+       help
+         The DS1307 driver suffered from a bug where it was enabling the
+         century bit inconditionnally but never used it when reading the time.
+         It made the driver unable to support dates beyond 2099.
+         Setting this option will add proper support for the century bit but if
+         the time was previously set using a kernel predating this option,
+         reading the date will return a date in the next century.
+         To solve that, you could boot a kernel without this option set, set
+         the RTC date and then boot a kernel with this option set.
+
 config RTC_DRV_DS1374
        tristate "Dallas/Maxim DS1374"
        help
@@ -374,16 +388,6 @@ config RTC_DRV_ISL12022
          This driver can also be built as a module. If so, the module
          will be called rtc-isl12022.
 
-config RTC_DRV_ISL12057
-       select REGMAP_I2C
-       tristate "Intersil ISL12057"
-       help
-         If you say yes here you get support for the Intersil ISL12057
-         I2C RTC chip.
-
-         This driver can also be built as a module. If so, the module
-         will be called rtc-isl12057.
-
 config RTC_DRV_X1205
        tristate "Xicor/Intersil X1205"
        help
@@ -661,6 +665,7 @@ config RTC_DRV_DS1343
          will be called rtc-ds1343.
 
 config RTC_DRV_DS1347
+       select REGMAP_SPI
        tristate "Dallas/Maxim DS1347"
        help
          If you say yes here you get support for the
@@ -1201,7 +1206,7 @@ comment "on-CPU RTC drivers"
 
 config RTC_DRV_ASM9260
        tristate "Alphascale asm9260 RTC"
-       depends on MACH_ASM9260
+       depends on MACH_ASM9260 || COMPILE_TEST
        help
          If you say yes here you get support for the RTC on the
          Alphascale asm9260 SoC.
@@ -1241,6 +1246,9 @@ config RTC_DRV_IMXDI
 config RTC_DRV_OMAP
        tristate "TI OMAP Real Time Clock"
        depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST
+       depends on OF
+       depends on PINCTRL
+       select GENERIC_PINCONF
        help
          Say "yes" here to support the on chip real time clock
          present on TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx.
index 8fb994bacdf7f155666d7f690a6f443b478b8fed..1ac694a330c8dadc0cbe78a4f6cd8d7359de2ecc 100644 (file)
@@ -72,7 +72,6 @@ obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
 obj-$(CONFIG_RTC_DRV_HYM8563)  += rtc-hym8563.o
 obj-$(CONFIG_RTC_DRV_IMXDI)    += rtc-imxdi.o
 obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
-obj-$(CONFIG_RTC_DRV_ISL12057) += rtc-isl12057.o
 obj-$(CONFIG_RTC_DRV_ISL1208)  += rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_JZ4740)   += rtc-jz4740.o
 obj-$(CONFIG_RTC_DRV_LP8788)   += rtc-lp8788.o
index 70b4fd0f61225485ebe87480037c3562793f09d4..9e336184491cbd1b1e80c47cddbf346c24665a63 100644 (file)
@@ -327,6 +327,8 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip)
                        .flags = 0,
                };
 
+               of_property_read_string_index(np, "clock-output-names",
+                                             i, &init.name);
                clk->regmap = chip->regmap;
                clk->offset = AC100_CLKOUT_CTRL1 + i;
                clk->hw.init = &init;
@@ -552,6 +554,9 @@ static int ac100_rtc_probe(struct platform_device *pdev)
        int ret;
 
        chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
        platform_set_drvdata(pdev, chip);
        chip->dev = &pdev->dev;
        chip->regmap = ac100->regmap;
index 5219916ce11d2b609bde574a4b99f72d279eb8e9..18a93d3e3f9317286c99f9718ea1d42ea95ebd50 100644 (file)
@@ -112,8 +112,6 @@ struct asm9260_rtc_priv {
        void __iomem            *iobase;
        struct rtc_device       *rtc;
        struct clk              *clk;
-       /* io lock */
-       spinlock_t              lock;
 };
 
 static irqreturn_t asm9260_rtc_irq(int irq, void *dev_id)
@@ -122,11 +120,15 @@ static irqreturn_t asm9260_rtc_irq(int irq, void *dev_id)
        u32 isr;
        unsigned long events = 0;
 
+       mutex_lock(&priv->rtc->ops_lock);
        isr = ioread32(priv->iobase + HW_CIIR);
-       if (!isr)
+       if (!isr) {
+               mutex_unlock(&priv->rtc->ops_lock);
                return IRQ_NONE;
+       }
 
        iowrite32(0, priv->iobase + HW_CIIR);
+       mutex_unlock(&priv->rtc->ops_lock);
 
        events |= RTC_AF | RTC_IRQF;
 
@@ -139,9 +141,7 @@ static int asm9260_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
        u32 ctime0, ctime1, ctime2;
-       unsigned long irq_flags;
 
-       spin_lock_irqsave(&priv->lock, irq_flags);
        ctime0 = ioread32(priv->iobase + HW_CTIME0);
        ctime1 = ioread32(priv->iobase + HW_CTIME1);
        ctime2 = ioread32(priv->iobase + HW_CTIME2);
@@ -155,7 +155,6 @@ static int asm9260_rtc_read_time(struct device *dev, struct rtc_time *tm)
                ctime1 = ioread32(priv->iobase + HW_CTIME1);
                ctime2 = ioread32(priv->iobase + HW_CTIME2);
        }
-       spin_unlock_irqrestore(&priv->lock, irq_flags);
 
        tm->tm_sec  = (ctime0 >> BM_CTIME0_SEC_S)  & BM_CTIME0_SEC_M;
        tm->tm_min  = (ctime0 >> BM_CTIME0_MIN_S)  & BM_CTIME0_MIN_M;
@@ -174,9 +173,7 @@ static int asm9260_rtc_read_time(struct device *dev, struct rtc_time *tm)
 static int asm9260_rtc_set_time(struct device *dev, struct rtc_time *tm)
 {
        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
-       unsigned long irq_flags;
 
-       spin_lock_irqsave(&priv->lock, irq_flags);
        /*
         * make sure SEC counter will not flip other counter on write time,
         * real value will be written at the enf of sequence.
@@ -191,7 +188,6 @@ static int asm9260_rtc_set_time(struct device *dev, struct rtc_time *tm)
        iowrite32(tm->tm_hour, priv->iobase + HW_HOUR);
        iowrite32(tm->tm_min,  priv->iobase + HW_MIN);
        iowrite32(tm->tm_sec,  priv->iobase + HW_SEC);
-       spin_unlock_irqrestore(&priv->lock, irq_flags);
 
        return 0;
 }
@@ -199,9 +195,7 @@ static int asm9260_rtc_set_time(struct device *dev, struct rtc_time *tm)
 static int asm9260_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 {
        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
-       unsigned long irq_flags;
 
-       spin_lock_irqsave(&priv->lock, irq_flags);
        alrm->time.tm_year = ioread32(priv->iobase + HW_ALYEAR);
        alrm->time.tm_mon  = ioread32(priv->iobase + HW_ALMON);
        alrm->time.tm_mday = ioread32(priv->iobase + HW_ALDOM);
@@ -213,7 +207,6 @@ static int asm9260_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 
        alrm->enabled = ioread32(priv->iobase + HW_AMR) ? 1 : 0;
        alrm->pending = ioread32(priv->iobase + HW_CIIR) ? 1 : 0;
-       spin_unlock_irqrestore(&priv->lock, irq_flags);
 
        return rtc_valid_tm(&alrm->time);
 }
@@ -221,9 +214,7 @@ static int asm9260_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 static int asm9260_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 {
        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
-       unsigned long irq_flags;
 
-       spin_lock_irqsave(&priv->lock, irq_flags);
        iowrite32(alrm->time.tm_year, priv->iobase + HW_ALYEAR);
        iowrite32(alrm->time.tm_mon,  priv->iobase + HW_ALMON);
        iowrite32(alrm->time.tm_mday, priv->iobase + HW_ALDOM);
@@ -234,7 +225,6 @@ static int asm9260_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        iowrite32(alrm->time.tm_sec,  priv->iobase + HW_ALSEC);
 
        iowrite32(alrm->enabled ? 0 : BM_AMR_OFF, priv->iobase + HW_AMR);
-       spin_unlock_irqrestore(&priv->lock, irq_flags);
 
        return 0;
 }
index 83ac2337c0f732df3cf29a4bca131e39fd37e470..de8bf56a41e7676d47e77bf391030e1adccc26e9 100644 (file)
@@ -187,7 +187,7 @@ static irqreturn_t at32_rtc_interrupt(int irq, void *dev_id)
        return ret;
 }
 
-static struct rtc_class_ops at32_rtc_ops = {
+static const struct rtc_class_ops at32_rtc_ops = {
        .read_time      = at32_rtc_readtime,
        .set_time       = at32_rtc_settime,
        .read_alarm     = at32_rtc_readalarm,
index 0299988b4f136812bad0e35c4943826608d5a114..397742446007aa2479969969c60097ad3edb6889 100644 (file)
@@ -93,8 +93,15 @@ static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm)
        if (error)
                return error;
 
+       /*
+        * In case of oscillator failure, the register contents should be
+        * considered invalid. The flag is cleared the next time the RTC is set.
+        */
+       if (regs.minutes & BQ32K_OF)
+               return -EINVAL;
+
        tm->tm_sec = bcd2bin(regs.seconds & BQ32K_SECONDS_MASK);
-       tm->tm_min = bcd2bin(regs.minutes & BQ32K_SECONDS_MASK);
+       tm->tm_min = bcd2bin(regs.minutes & BQ32K_MINUTES_MASK);
        tm->tm_hour = bcd2bin(regs.cent_hours & BQ32K_HOURS_MASK);
        tm->tm_mday = bcd2bin(regs.date);
        tm->tm_wday = bcd2bin(regs.day) - 1;
@@ -204,13 +211,10 @@ static int bq32k_probe(struct i2c_client *client,
 
        /* Check Oscillator Failure flag */
        error = bq32k_read(dev, &reg, BQ32K_MINUTES, 1);
-       if (!error && (reg & BQ32K_OF)) {
-               dev_warn(dev, "Oscillator Failure. Check RTC battery.\n");
-               reg &= ~BQ32K_OF;
-               error = bq32k_write(dev, &reg, BQ32K_MINUTES, 1);
-       }
        if (error)
                return error;
+       if (reg & BQ32K_OF)
+               dev_warn(dev, "Oscillator Failure. Check RTC battery.\n");
 
        if (client->dev.of_node)
                trickle_charger_of_init(dev, client->dev.of_node);
index 43745cac0141a4445dda3abe6a31d8ca172355b0..dd3d59806ffa02ef955660390533881d9f6cee62 100644 (file)
@@ -62,6 +62,8 @@ struct cmos_rtc {
        u8                      day_alrm;
        u8                      mon_alrm;
        u8                      century;
+
+       struct rtc_wkalrm       saved_wkalrm;
 };
 
 /* both platform and pnp busses use negative numbers for invalid irqs */
@@ -707,6 +709,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
                goto cleanup1;
        }
 
+       hpet_rtc_timer_init();
+
        if (is_valid_irq(rtc_irq)) {
                irq_handler_t rtc_cmos_int_handler;
 
@@ -714,6 +718,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
                        rtc_cmos_int_handler = hpet_rtc_interrupt;
                        retval = hpet_register_irq_handler(cmos_interrupt);
                        if (retval) {
+                               hpet_mask_rtc_irq_bit(RTC_IRQMASK);
                                dev_warn(dev, "hpet_register_irq_handler "
                                                " failed in rtc_init().");
                                goto cleanup1;
@@ -729,7 +734,6 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
                        goto cleanup1;
                }
        }
-       hpet_rtc_timer_init();
 
        /* export at least the first block of NVRAM */
        nvram.size = address_space - NVRAM_OFFSET;
@@ -844,8 +848,6 @@ static int cmos_aie_poweroff(struct device *dev)
        return retval;
 }
 
-#ifdef CONFIG_PM
-
 static int cmos_suspend(struct device *dev)
 {
        struct cmos_rtc *cmos = dev_get_drvdata(dev);
@@ -877,6 +879,8 @@ static int cmos_suspend(struct device *dev)
                        enable_irq_wake(cmos->irq);
        }
 
+       cmos_read_alarm(dev, &cmos->saved_wkalrm);
+
        dev_dbg(dev, "suspend%s, ctrl %02x\n",
                        (tmp & RTC_AIE) ? ", alarm may wake" : "",
                        tmp);
@@ -892,12 +896,32 @@ static int cmos_suspend(struct device *dev)
  */
 static inline int cmos_poweroff(struct device *dev)
 {
+       if (!IS_ENABLED(CONFIG_PM))
+               return -ENOSYS;
+
        return cmos_suspend(dev);
 }
 
-#ifdef CONFIG_PM_SLEEP
+static void cmos_check_wkalrm(struct device *dev)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       struct rtc_wkalrm current_alarm;
+       time64_t t_current_expires;
+       time64_t t_saved_expires;
+
+       cmos_read_alarm(dev, &current_alarm);
+       t_current_expires = rtc_tm_to_time64(&current_alarm.time);
+       t_saved_expires = rtc_tm_to_time64(&cmos->saved_wkalrm.time);
+       if (t_current_expires != t_saved_expires ||
+           cmos->saved_wkalrm.enabled != current_alarm.enabled) {
+               cmos_set_alarm(dev, &cmos->saved_wkalrm);
+       }
+}
+
+static void cmos_check_acpi_rtc_status(struct device *dev,
+                                      unsigned char *rtc_control);
 
-static int cmos_resume(struct device *dev)
+static int __maybe_unused cmos_resume(struct device *dev)
 {
        struct cmos_rtc *cmos = dev_get_drvdata(dev);
        unsigned char tmp;
@@ -910,6 +934,9 @@ static int cmos_resume(struct device *dev)
                cmos->enabled_wake = 0;
        }
 
+       /* The BIOS might have changed the alarm, restore it */
+       cmos_check_wkalrm(dev);
+
        spin_lock_irq(&rtc_lock);
        tmp = cmos->suspend_ctrl;
        cmos->suspend_ctrl = 0;
@@ -936,6 +963,9 @@ static int cmos_resume(struct device *dev)
                        tmp &= ~RTC_AIE;
                        hpet_mask_rtc_irq_bit(RTC_AIE);
                } while (mask & RTC_AIE);
+
+               if (tmp & RTC_AIE)
+                       cmos_check_acpi_rtc_status(dev, &tmp);
        }
        spin_unlock_irq(&rtc_lock);
 
@@ -944,16 +974,6 @@ static int cmos_resume(struct device *dev)
        return 0;
 }
 
-#endif
-#else
-
-static inline int cmos_poweroff(struct device *dev)
-{
-       return -ENOSYS;
-}
-
-#endif
-
 static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);
 
 /*----------------------------------------------------------------*/
@@ -973,6 +993,20 @@ static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);
 static u32 rtc_handler(void *context)
 {
        struct device *dev = context;
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       unsigned char rtc_control = 0;
+       unsigned char rtc_intr;
+
+       spin_lock_irq(&rtc_lock);
+       if (cmos_rtc.suspend_ctrl)
+               rtc_control = CMOS_READ(RTC_CONTROL);
+       if (rtc_control & RTC_AIE) {
+               cmos_rtc.suspend_ctrl &= ~RTC_AIE;
+               CMOS_WRITE(rtc_control, RTC_CONTROL);
+               rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
+               rtc_update_irq(cmos->rtc, 1, rtc_intr);
+       }
+       spin_unlock_irq(&rtc_lock);
 
        pm_wakeup_event(dev, 0);
        acpi_clear_event(ACPI_EVENT_RTC);
@@ -1039,12 +1073,39 @@ static void cmos_wake_setup(struct device *dev)
        device_init_wakeup(dev, 1);
 }
 
+static void cmos_check_acpi_rtc_status(struct device *dev,
+                                      unsigned char *rtc_control)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       acpi_event_status rtc_status;
+       acpi_status status;
+
+       if (acpi_gbl_FADT.flags & ACPI_FADT_FIXED_RTC)
+               return;
+
+       status = acpi_get_event_status(ACPI_EVENT_RTC, &rtc_status);
+       if (ACPI_FAILURE(status)) {
+               dev_err(dev, "Could not get RTC status\n");
+       } else if (rtc_status & ACPI_EVENT_FLAG_SET) {
+               unsigned char mask;
+               *rtc_control &= ~RTC_AIE;
+               CMOS_WRITE(*rtc_control, RTC_CONTROL);
+               mask = CMOS_READ(RTC_INTR_FLAGS);
+               rtc_update_irq(cmos->rtc, 1, mask);
+       }
+}
+
 #else
 
 static void cmos_wake_setup(struct device *dev)
 {
 }
 
+static void cmos_check_acpi_rtc_status(struct device *dev,
+                                      unsigned char *rtc_control)
+{
+}
+
 #endif
 
 #ifdef CONFIG_PNP
@@ -1206,9 +1267,7 @@ static struct platform_driver cmos_platform_driver = {
        .shutdown       = cmos_platform_shutdown,
        .driver = {
                .name           = driver_name,
-#ifdef CONFIG_PM
                .pm             = &cmos_pm_ops,
-#endif
                .of_match_table = of_match_ptr(of_cmos_match),
        }
 };
index 101b7a240e0fa8e482ef140b921c5b31811e829a..cfc4141d99cde18def976813feba11113111a378 100644 (file)
@@ -140,7 +140,7 @@ static int coh901331_alarm_irq_enable(struct device *dev, unsigned int enabled)
        return 0;
 }
 
-static struct rtc_class_ops coh901331_ops = {
+static const struct rtc_class_ops coh901331_ops = {
        .read_time = coh901331_read_time,
        .set_mmss = coh901331_set_mmss,
        .read_alarm = coh901331_read_alarm,
index dba60c1dfce2ee9f71fefc6b023a8eee799e63d2..caf35567e14cbe1966b844ee7d6dd6012690ccdd 100644 (file)
@@ -469,7 +469,7 @@ static int davinci_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
        return 0;
 }
 
-static struct rtc_class_ops davinci_rtc_ops = {
+static const struct rtc_class_ops davinci_rtc_ops = {
        .ioctl                  = davinci_rtc_ioctl,
        .read_time              = davinci_rtc_read_time,
        .set_time               = davinci_rtc_set_time,
index 8d05596a6765915373edabfb5048d1e2a2457854..b253bf1b35314776ee3df0b9fc68c7c2f544ce78 100644 (file)
@@ -159,7 +159,7 @@ static int dc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
        return 0;
 }
 
-static struct rtc_class_ops dc_rtc_ops = {
+static const struct rtc_class_ops dc_rtc_ops = {
        .read_time              = dc_rtc_read_time,
        .set_mmss               = dc_rtc_set_mmss,
        .read_alarm             = dc_rtc_read_alarm,
index f5dd09fe5add5e65a40d9d178f0283a30c9e6548..0ec4be62322bfaf7695eb99aa6efd428264a3771 100644 (file)
@@ -102,7 +102,7 @@ static int ds1302_rtc_get_time(struct device *dev, struct rtc_time *time)
        return rtc_valid_tm(time);
 }
 
-static struct rtc_class_ops ds1302_rtc_ops = {
+static const struct rtc_class_ops ds1302_rtc_ops = {
        .read_time      = ds1302_rtc_get_time,
        .set_time       = ds1302_rtc_set_time,
 };
index 8e1c5cb6ece6f60619e5cafcea4271e056d96778..4e31036ee2596dec93accd26f627c5b95591ae9f 100644 (file)
@@ -186,6 +186,7 @@ static const struct i2c_device_id ds1307_id[] = {
        { "mcp7941x", mcp794xx },
        { "pt7c4338", ds_1307 },
        { "rx8025", rx_8025 },
+       { "isl12057", ds_1337 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ds1307_id);
@@ -382,10 +383,25 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
        t->tm_mday = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f);
        tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f;
        t->tm_mon = bcd2bin(tmp) - 1;
-
-       /* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */
        t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100;
 
+#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
+       switch (ds1307->type) {
+       case ds_1337:
+       case ds_1339:
+       case ds_3231:
+               if (ds1307->regs[DS1307_REG_MONTH] & DS1337_BIT_CENTURY)
+                       t->tm_year += 100;
+               break;
+       case ds_1340:
+               if (ds1307->regs[DS1307_REG_HOUR] & DS1340_BIT_CENTURY)
+                       t->tm_year += 100;
+               break;
+       default:
+               break;
+       }
+#endif
+
        dev_dbg(dev, "%s secs=%d, mins=%d, "
                "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
                "read", t->tm_sec, t->tm_min,
@@ -409,6 +425,27 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
                t->tm_hour, t->tm_mday,
                t->tm_mon, t->tm_year, t->tm_wday);
 
+#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
+       if (t->tm_year < 100)
+               return -EINVAL;
+
+       switch (ds1307->type) {
+       case ds_1337:
+       case ds_1339:
+       case ds_3231:
+       case ds_1340:
+               if (t->tm_year > 299)
+                       return -EINVAL;
+       default:
+               if (t->tm_year > 199)
+                       return -EINVAL;
+               break;
+       }
+#else
+       if (t->tm_year < 100 || t->tm_year > 199)
+               return -EINVAL;
+#endif
+
        buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec);
        buf[DS1307_REG_MIN] = bin2bcd(t->tm_min);
        buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);
@@ -424,11 +461,13 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
        case ds_1337:
        case ds_1339:
        case ds_3231:
-               buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
+               if (t->tm_year > 199)
+                       buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
                break;
        case ds_1340:
-               buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN
-                               | DS1340_BIT_CENTURY;
+               buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN;
+               if (t->tm_year > 199)
+                       buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY;
                break;
        case mcp794xx:
                /*
@@ -1295,6 +1334,11 @@ static int ds1307_probe(struct i2c_client *client,
        if (of_property_read_bool(client->dev.of_node, "wakeup-source")) {
                ds1307_can_wakeup_device = true;
        }
+       /* Intersil ISL12057 DT backward compatibility */
+       if (of_property_read_bool(client->dev.of_node,
+                                 "isil,irq2-can-wakeup-machine")) {
+               ds1307_can_wakeup_device = true;
+       }
 #endif
 
        switch (ds1307->type) {
index 641e8e8a0dd7251f677e9146c098e7ce475fa516..ccfc9d43eb1e680956d32892e9bacfaddefafd1a 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/rtc.h>
 #include <linux/spi/spi.h>
 #include <linux/bcd.h>
+#include <linux/regmap.h>
 
 /* Registers in ds1347 rtc */
 
 #define DS1347_STATUS_REG      0x17
 #define DS1347_CLOCK_BURST     0x3F
 
-static int ds1347_read_reg(struct device *dev, unsigned char address,
-                               unsigned char *data)
-{
-       struct spi_device *spi = to_spi_device(dev);
-
-       *data = address | 0x80;
-
-       return spi_write_then_read(spi, data, 1, data, 1);
-}
-
-static int ds1347_write_reg(struct device *dev, unsigned char address,
-                               unsigned char data)
-{
-       struct spi_device *spi = to_spi_device(dev);
-       unsigned char buf[2];
-
-       buf[0] = address & 0x7F;
-       buf[1] = data;
+static const struct regmap_range ds1347_ranges[] = {
+       {
+               .range_min = DS1347_SECONDS_REG,
+               .range_max = DS1347_STATUS_REG,
+       },
+};
 
-       return spi_write_then_read(spi, buf, 2, NULL, 0);
-}
+static const struct regmap_access_table ds1347_access_table = {
+       .yes_ranges = ds1347_ranges,
+       .n_yes_ranges = ARRAY_SIZE(ds1347_ranges),
+};
 
 static int ds1347_read_time(struct device *dev, struct rtc_time *dt)
 {
        struct spi_device *spi = to_spi_device(dev);
+       struct regmap *map;
        int err;
        unsigned char buf[8];
 
-       buf[0] = DS1347_CLOCK_BURST | 0x80;
+       map = spi_get_drvdata(spi);
 
-       err = spi_write_then_read(spi, buf, 1, buf, 8);
+       err = regmap_bulk_read(map, DS1347_CLOCK_BURST, buf, 8);
        if (err)
                return err;
 
@@ -80,25 +72,27 @@ static int ds1347_read_time(struct device *dev, struct rtc_time *dt)
 static int ds1347_set_time(struct device *dev, struct rtc_time *dt)
 {
        struct spi_device *spi = to_spi_device(dev);
-       unsigned char buf[9];
+       struct regmap *map;
+       unsigned char buf[8];
+
+       map = spi_get_drvdata(spi);
 
-       buf[0] = DS1347_CLOCK_BURST & 0x7F;
-       buf[1] = bin2bcd(dt->tm_sec);
-       buf[2] = bin2bcd(dt->tm_min);
-       buf[3] = (bin2bcd(dt->tm_hour) & 0x3F);
-       buf[4] = bin2bcd(dt->tm_mday);
-       buf[5] = bin2bcd(dt->tm_mon + 1);
-       buf[6] = bin2bcd(dt->tm_wday + 1);
+       buf[0] = bin2bcd(dt->tm_sec);
+       buf[1] = bin2bcd(dt->tm_min);
+       buf[2] = (bin2bcd(dt->tm_hour) & 0x3F);
+       buf[3] = bin2bcd(dt->tm_mday);
+       buf[4] = bin2bcd(dt->tm_mon + 1);
+       buf[5] = bin2bcd(dt->tm_wday + 1);
 
        /* year in linux is from 1900 i.e in range of 100
        in rtc it is from 00 to 99 */
        dt->tm_year = dt->tm_year % 100;
 
-       buf[7] = bin2bcd(dt->tm_year);
-       buf[8] = bin2bcd(0x00);
+       buf[6] = bin2bcd(dt->tm_year);
+       buf[7] = bin2bcd(0x00);
 
        /* write the rtc settings */
-       return spi_write_then_read(spi, buf, 9, NULL, 0);
+       return regmap_bulk_write(map, DS1347_CLOCK_BURST, buf, 8);
 }
 
 static const struct rtc_class_ops ds1347_rtc_ops = {
@@ -109,35 +103,53 @@ static const struct rtc_class_ops ds1347_rtc_ops = {
 static int ds1347_probe(struct spi_device *spi)
 {
        struct rtc_device *rtc;
-       unsigned char data;
+       struct regmap_config config;
+       struct regmap *map;
+       unsigned int data;
        int res;
 
+       memset(&config, 0, sizeof(config));
+       config.reg_bits = 8;
+       config.val_bits = 8;
+       config.read_flag_mask = 0x80;
+       config.max_register = 0x3F;
+       config.wr_table = &ds1347_access_table;
+
        /* spi setup with ds1347 in mode 3 and bits per word as 8 */
        spi->mode = SPI_MODE_3;
        spi->bits_per_word = 8;
        spi_setup(spi);
 
+       map = devm_regmap_init_spi(spi, &config);
+
+       if (IS_ERR(map)) {
+               dev_err(&spi->dev, "ds1347 regmap init spi failed\n");
+               return PTR_ERR(map);
+       }
+
+       spi_set_drvdata(spi, map);
+
        /* RTC Settings */
-       res = ds1347_read_reg(&spi->dev, DS1347_SECONDS_REG, &data);
+       res = regmap_read(map, DS1347_SECONDS_REG, &data);
        if (res)
                return res;
 
        /* Disable the write protect of rtc */
-       ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data);
+       regmap_read(map, DS1347_CONTROL_REG, &data);
        data = data & ~(1<<7);
-       ds1347_write_reg(&spi->dev, DS1347_CONTROL_REG, data);
+       regmap_write(map, DS1347_CONTROL_REG, data);
 
        /* Enable the oscillator , disable the oscillator stop flag,
         and glitch filter to reduce current consumption */
-       ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data);
+       regmap_read(map, DS1347_STATUS_REG, &data);
        data = data & 0x1B;
-       ds1347_write_reg(&spi->dev, DS1347_STATUS_REG, data);
+       regmap_write(map, DS1347_STATUS_REG, data);
 
        /* display the settings */
-       ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data);
+       regmap_read(map, DS1347_CONTROL_REG, &data);
        dev_info(&spi->dev, "DS1347 RTC CTRL Reg = 0x%02x\n", data);
 
-       ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data);
+       regmap_read(map, DS1347_STATUS_REG, &data);
        dev_info(&spi->dev, "DS1347 RTC Status Reg = 0x%02x\n", data);
 
        rtc = devm_rtc_device_register(&spi->dev, "ds1347",
@@ -146,8 +158,6 @@ static int ds1347_probe(struct spi_device *spi)
        if (IS_ERR(rtc))
                return PTR_ERR(rtc);
 
-       spi_set_drvdata(spi, rtc);
-
        return 0;
 }
 
index b57505efadbc85475bea0c6c11fa8c7115694dad..688debc143483ff33aef70c08c85656a093b0fc9 100644 (file)
@@ -110,7 +110,7 @@ static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm)
        return 0;
 }
 
-static struct rtc_class_ops gemini_rtc_ops = {
+static const struct rtc_class_ops gemini_rtc_ops = {
        .read_time     = gemini_rtc_read_time,
        .set_time      = gemini_rtc_set_time,
 };
diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c
deleted file mode 100644 (file)
index 0e7f0f5..0000000
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
- * rtc-isl12057 - Driver for Intersil ISL12057 I2C Real Time Clock
- *
- * Copyright (C) 2013, Arnaud EBALARD <arno@natisbad.org>
- *
- * This work is largely based on Intersil ISL1208 driver developed by
- * Hebert Valerio Riedel <hvr@gnu.org>.
- *
- * Detailed datasheet on which this development is based is available here:
- *
- *  http://natisbad.org/NAS2/refs/ISL12057.pdf
- *
- * 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/mutex.h>
-#include <linux/rtc.h>
-#include <linux/i2c.h>
-#include <linux/bcd.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/regmap.h>
-
-#define DRV_NAME "rtc-isl12057"
-
-/* RTC section */
-#define ISL12057_REG_RTC_SC    0x00    /* Seconds */
-#define ISL12057_REG_RTC_MN    0x01    /* Minutes */
-#define ISL12057_REG_RTC_HR    0x02    /* Hours */
-#define ISL12057_REG_RTC_HR_PM BIT(5)  /* AM/PM bit in 12h format */
-#define ISL12057_REG_RTC_HR_MIL BIT(6) /* 24h/12h format */
-#define ISL12057_REG_RTC_DW    0x03    /* Day of the Week */
-#define ISL12057_REG_RTC_DT    0x04    /* Date */
-#define ISL12057_REG_RTC_MO    0x05    /* Month */
-#define ISL12057_REG_RTC_MO_CEN        BIT(7)  /* Century bit */
-#define ISL12057_REG_RTC_YR    0x06    /* Year */
-#define ISL12057_RTC_SEC_LEN   7
-
-/* Alarm 1 section */
-#define ISL12057_REG_A1_SC     0x07    /* Alarm 1 Seconds */
-#define ISL12057_REG_A1_MN     0x08    /* Alarm 1 Minutes */
-#define ISL12057_REG_A1_HR     0x09    /* Alarm 1 Hours */
-#define ISL12057_REG_A1_HR_PM  BIT(5)  /* AM/PM bit in 12h format */
-#define ISL12057_REG_A1_HR_MIL BIT(6)  /* 24h/12h format */
-#define ISL12057_REG_A1_DWDT   0x0A    /* Alarm 1 Date / Day of the week */
-#define ISL12057_REG_A1_DWDT_B BIT(6)  /* DW / DT selection bit */
-#define ISL12057_A1_SEC_LEN    4
-
-/* Alarm 2 section */
-#define ISL12057_REG_A2_MN     0x0B    /* Alarm 2 Minutes */
-#define ISL12057_REG_A2_HR     0x0C    /* Alarm 2 Hours */
-#define ISL12057_REG_A2_DWDT   0x0D    /* Alarm 2 Date / Day of the week */
-#define ISL12057_A2_SEC_LEN    3
-
-/* Control/Status registers */
-#define ISL12057_REG_INT       0x0E
-#define ISL12057_REG_INT_A1IE  BIT(0)  /* Alarm 1 interrupt enable bit */
-#define ISL12057_REG_INT_A2IE  BIT(1)  /* Alarm 2 interrupt enable bit */
-#define ISL12057_REG_INT_INTCN BIT(2)  /* Interrupt control enable bit */
-#define ISL12057_REG_INT_RS1   BIT(3)  /* Freq out control bit 1 */
-#define ISL12057_REG_INT_RS2   BIT(4)  /* Freq out control bit 2 */
-#define ISL12057_REG_INT_EOSC  BIT(7)  /* Oscillator enable bit */
-
-#define ISL12057_REG_SR                0x0F
-#define ISL12057_REG_SR_A1F    BIT(0)  /* Alarm 1 interrupt bit */
-#define ISL12057_REG_SR_A2F    BIT(1)  /* Alarm 2 interrupt bit */
-#define ISL12057_REG_SR_OSF    BIT(7)  /* Oscillator failure bit */
-
-/* Register memory map length */
-#define ISL12057_MEM_MAP_LEN   0x10
-
-struct isl12057_rtc_data {
-       struct rtc_device *rtc;
-       struct regmap *regmap;
-       struct mutex lock;
-       int irq;
-};
-
-static void isl12057_rtc_regs_to_tm(struct rtc_time *tm, u8 *regs)
-{
-       tm->tm_sec = bcd2bin(regs[ISL12057_REG_RTC_SC]);
-       tm->tm_min = bcd2bin(regs[ISL12057_REG_RTC_MN]);
-
-       if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_MIL) { /* AM/PM */
-               tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x1f);
-               if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_PM)
-                       tm->tm_hour += 12;
-       } else {                                            /* 24 hour mode */
-               tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x3f);
-       }
-
-       tm->tm_mday = bcd2bin(regs[ISL12057_REG_RTC_DT]);
-       tm->tm_wday = bcd2bin(regs[ISL12057_REG_RTC_DW]) - 1; /* starts at 1 */
-       tm->tm_mon  = bcd2bin(regs[ISL12057_REG_RTC_MO] & 0x1f) - 1; /* ditto */
-       tm->tm_year = bcd2bin(regs[ISL12057_REG_RTC_YR]) + 100;
-
-       /* Check if years register has overflown from 99 to 00 */
-       if (regs[ISL12057_REG_RTC_MO] & ISL12057_REG_RTC_MO_CEN)
-               tm->tm_year += 100;
-}
-
-static int isl12057_rtc_tm_to_regs(u8 *regs, struct rtc_time *tm)
-{
-       u8 century_bit;
-
-       /*
-        * The clock has an 8 bit wide bcd-coded register for the year.
-        * It also has a century bit encoded in MO flag which provides
-        * information about overflow of year register from 99 to 00.
-        * tm_year is an offset from 1900 and we are interested in the
-        * 2000-2199 range, so any value less than 100 or larger than
-        * 299 is invalid.
-        */
-       if (tm->tm_year < 100 || tm->tm_year > 299)
-               return -EINVAL;
-
-       century_bit = (tm->tm_year > 199) ? ISL12057_REG_RTC_MO_CEN : 0;
-
-       regs[ISL12057_REG_RTC_SC] = bin2bcd(tm->tm_sec);
-       regs[ISL12057_REG_RTC_MN] = bin2bcd(tm->tm_min);
-       regs[ISL12057_REG_RTC_HR] = bin2bcd(tm->tm_hour); /* 24-hour format */
-       regs[ISL12057_REG_RTC_DT] = bin2bcd(tm->tm_mday);
-       regs[ISL12057_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1) | century_bit;
-       regs[ISL12057_REG_RTC_YR] = bin2bcd(tm->tm_year % 100);
-       regs[ISL12057_REG_RTC_DW] = bin2bcd(tm->tm_wday + 1);
-
-       return 0;
-}
-
-/*
- * Try and match register bits w/ fixed null values to see whether we
- * are dealing with an ISL12057. Note: this function is called early
- * during init and hence does need mutex protection.
- */
-static int isl12057_i2c_validate_chip(struct regmap *regmap)
-{
-       u8 regs[ISL12057_MEM_MAP_LEN];
-       static const u8 mask[ISL12057_MEM_MAP_LEN] = { 0x80, 0x80, 0x80, 0xf8,
-                                                      0xc0, 0x60, 0x00, 0x00,
-                                                      0x00, 0x00, 0x00, 0x00,
-                                                      0x00, 0x00, 0x60, 0x7c };
-       int ret, i;
-
-       ret = regmap_bulk_read(regmap, 0, regs, ISL12057_MEM_MAP_LEN);
-       if (ret)
-               return ret;
-
-       for (i = 0; i < ISL12057_MEM_MAP_LEN; ++i) {
-               if (regs[i] & mask[i])  /* check if bits are cleared */
-                       return -ENODEV;
-       }
-
-       return 0;
-}
-
-static int _isl12057_rtc_clear_alarm(struct device *dev)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       int ret;
-
-       ret = regmap_update_bits(data->regmap, ISL12057_REG_SR,
-                                ISL12057_REG_SR_A1F, 0);
-       if (ret)
-               dev_err(dev, "%s: clearing alarm failed (%d)\n", __func__, ret);
-
-       return ret;
-}
-
-static int _isl12057_rtc_update_alarm(struct device *dev, int enable)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       int ret;
-
-       ret = regmap_update_bits(data->regmap, ISL12057_REG_INT,
-                                ISL12057_REG_INT_A1IE,
-                                enable ? ISL12057_REG_INT_A1IE : 0);
-       if (ret)
-               dev_err(dev, "%s: changing alarm interrupt flag failed (%d)\n",
-                       __func__, ret);
-
-       return ret;
-}
-
-/*
- * Note: as we only read from device and do not perform any update, there is
- * no need for an equivalent function which would try and get driver's main
- * lock. Here, it is safe for everyone if we just use regmap internal lock
- * on the device when reading.
- */
-static int _isl12057_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       u8 regs[ISL12057_RTC_SEC_LEN];
-       unsigned int sr;
-       int ret;
-
-       ret = regmap_read(data->regmap, ISL12057_REG_SR, &sr);
-       if (ret) {
-               dev_err(dev, "%s: unable to read oscillator status flag (%d)\n",
-                       __func__, ret);
-               goto out;
-       } else {
-               if (sr & ISL12057_REG_SR_OSF) {
-                       ret = -ENODATA;
-                       goto out;
-               }
-       }
-
-       ret = regmap_bulk_read(data->regmap, ISL12057_REG_RTC_SC, regs,
-                              ISL12057_RTC_SEC_LEN);
-       if (ret)
-               dev_err(dev, "%s: unable to read RTC time section (%d)\n",
-                       __func__, ret);
-
-out:
-       if (ret)
-               return ret;
-
-       isl12057_rtc_regs_to_tm(tm, regs);
-
-       return rtc_valid_tm(tm);
-}
-
-static int isl12057_rtc_update_alarm(struct device *dev, int enable)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       int ret;
-
-       mutex_lock(&data->lock);
-       ret = _isl12057_rtc_update_alarm(dev, enable);
-       mutex_unlock(&data->lock);
-
-       return ret;
-}
-
-static int isl12057_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       struct rtc_time *alarm_tm = &alarm->time;
-       u8 regs[ISL12057_A1_SEC_LEN];
-       unsigned int ir;
-       int ret;
-
-       mutex_lock(&data->lock);
-       ret = regmap_bulk_read(data->regmap, ISL12057_REG_A1_SC, regs,
-                              ISL12057_A1_SEC_LEN);
-       if (ret) {
-               dev_err(dev, "%s: reading alarm section failed (%d)\n",
-                       __func__, ret);
-               goto err_unlock;
-       }
-
-       alarm_tm->tm_sec  = bcd2bin(regs[0] & 0x7f);
-       alarm_tm->tm_min  = bcd2bin(regs[1] & 0x7f);
-       alarm_tm->tm_hour = bcd2bin(regs[2] & 0x3f);
-       alarm_tm->tm_mday = bcd2bin(regs[3] & 0x3f);
-
-       ret = regmap_read(data->regmap, ISL12057_REG_INT, &ir);
-       if (ret) {
-               dev_err(dev, "%s: reading alarm interrupt flag failed (%d)\n",
-                       __func__, ret);
-               goto err_unlock;
-       }
-
-       alarm->enabled = !!(ir & ISL12057_REG_INT_A1IE);
-
-err_unlock:
-       mutex_unlock(&data->lock);
-
-       return ret;
-}
-
-static int isl12057_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       struct rtc_time *alarm_tm = &alarm->time;
-       unsigned long rtc_secs, alarm_secs;
-       u8 regs[ISL12057_A1_SEC_LEN];
-       struct rtc_time rtc_tm;
-       int ret, enable = 1;
-
-       mutex_lock(&data->lock);
-       ret = _isl12057_rtc_read_time(dev, &rtc_tm);
-       if (ret)
-               goto err_unlock;
-
-       ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
-       if (ret)
-               goto err_unlock;
-
-       ret = rtc_tm_to_time(alarm_tm, &alarm_secs);
-       if (ret)
-               goto err_unlock;
-
-       /* If alarm time is before current time, disable the alarm */
-       if (!alarm->enabled || alarm_secs <= rtc_secs) {
-               enable = 0;
-       } else {
-               /*
-                * Chip only support alarms up to one month in the future. Let's
-                * return an error if we get something after that limit.
-                * Comparison is done by incrementing rtc_tm month field by one
-                * and checking alarm value is still below.
-                */
-               if (rtc_tm.tm_mon == 11) { /* handle year wrapping */
-                       rtc_tm.tm_mon = 0;
-                       rtc_tm.tm_year += 1;
-               } else {
-                       rtc_tm.tm_mon += 1;
-               }
-
-               ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
-               if (ret)
-                       goto err_unlock;
-
-               if (alarm_secs > rtc_secs) {
-                       dev_err(dev, "%s: max for alarm is one month (%d)\n",
-                               __func__, ret);
-                       ret = -EINVAL;
-                       goto err_unlock;
-               }
-       }
-
-       /* Disable the alarm before modifying it */
-       ret = _isl12057_rtc_update_alarm(dev, 0);
-       if (ret < 0) {
-               dev_err(dev, "%s: unable to disable the alarm (%d)\n",
-                       __func__, ret);
-               goto err_unlock;
-       }
-
-       /* Program alarm registers */
-       regs[0] = bin2bcd(alarm_tm->tm_sec) & 0x7f;
-       regs[1] = bin2bcd(alarm_tm->tm_min) & 0x7f;
-       regs[2] = bin2bcd(alarm_tm->tm_hour) & 0x3f;
-       regs[3] = bin2bcd(alarm_tm->tm_mday) & 0x3f;
-
-       ret = regmap_bulk_write(data->regmap, ISL12057_REG_A1_SC, regs,
-                               ISL12057_A1_SEC_LEN);
-       if (ret < 0) {
-               dev_err(dev, "%s: writing alarm section failed (%d)\n",
-                       __func__, ret);
-               goto err_unlock;
-       }
-
-       /* Enable or disable alarm */
-       ret = _isl12057_rtc_update_alarm(dev, enable);
-
-err_unlock:
-       mutex_unlock(&data->lock);
-
-       return ret;
-}
-
-static int isl12057_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       u8 regs[ISL12057_RTC_SEC_LEN];
-       int ret;
-
-       ret = isl12057_rtc_tm_to_regs(regs, tm);
-       if (ret)
-               return ret;
-
-       mutex_lock(&data->lock);
-       ret = regmap_bulk_write(data->regmap, ISL12057_REG_RTC_SC, regs,
-                               ISL12057_RTC_SEC_LEN);
-       if (ret) {
-               dev_err(dev, "%s: unable to write RTC time section (%d)\n",
-                       __func__, ret);
-               goto out;
-       }
-
-       /*
-        * Now that RTC time has been updated, let's clear oscillator
-        * failure flag, if needed.
-        */
-       ret = regmap_update_bits(data->regmap, ISL12057_REG_SR,
-                                ISL12057_REG_SR_OSF, 0);
-       if (ret < 0)
-               dev_err(dev, "%s: unable to clear osc. failure bit (%d)\n",
-                       __func__, ret);
-
-out:
-       mutex_unlock(&data->lock);
-
-       return ret;
-}
-
-/*
- * Check current RTC status and enable/disable what needs to be. Return 0 if
- * everything went ok and a negative value upon error. Note: this function
- * is called early during init and hence does need mutex protection.
- */
-static int isl12057_check_rtc_status(struct device *dev, struct regmap *regmap)
-{
-       int ret;
-
-       /* Enable oscillator if not already running */
-       ret = regmap_update_bits(regmap, ISL12057_REG_INT,
-                                ISL12057_REG_INT_EOSC, 0);
-       if (ret < 0) {
-               dev_err(dev, "%s: unable to enable oscillator (%d)\n",
-                       __func__, ret);
-               return ret;
-       }
-
-       /* Clear alarm bit if needed */
-       ret = regmap_update_bits(regmap, ISL12057_REG_SR,
-                                ISL12057_REG_SR_A1F, 0);
-       if (ret < 0) {
-               dev_err(dev, "%s: unable to clear alarm bit (%d)\n",
-                       __func__, ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-/*
- * One would expect the device to be marked as a wakeup source only
- * when an IRQ pin of the RTC is routed to an interrupt line of the
- * CPU. In practice, such an IRQ pin can be connected to a PMIC and
- * this allows the device to be powered up when RTC alarm rings. This
- * is for instance the case on ReadyNAS 102, 104 and 2120. On those
- * devices with no IRQ driectly connected to the SoC, the RTC chip
- * can be forced as a wakeup source by stating that explicitly in
- * the device's .dts file using the "wakeup-source" boolean property.
- * This will guarantee 'wakealarm' sysfs entry is available on the device.
- *
- * The function below returns 1, i.e. the capability of the chip to
- * wakeup the device, based on IRQ availability or if the boolean
- * property has been set in the .dts file. Otherwise, it returns 0.
- */
-
-static bool isl12057_can_wakeup_machine(struct device *dev)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-
-       return data->irq || of_property_read_bool(dev->of_node, "wakeup-source")
-               || of_property_read_bool(dev->of_node, /* legacy */
-                                        "isil,irq2-can-wakeup-machine");
-}
-#else
-static bool isl12057_can_wakeup_machine(struct device *dev)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-
-       return !!data->irq;
-}
-#endif
-
-static int isl12057_rtc_alarm_irq_enable(struct device *dev,
-                                        unsigned int enable)
-{
-       struct isl12057_rtc_data *rtc_data = dev_get_drvdata(dev);
-       int ret = -ENOTTY;
-
-       if (rtc_data->irq)
-               ret = isl12057_rtc_update_alarm(dev, enable);
-
-       return ret;
-}
-
-static irqreturn_t isl12057_rtc_interrupt(int irq, void *data)
-{
-       struct i2c_client *client = data;
-       struct isl12057_rtc_data *rtc_data = dev_get_drvdata(&client->dev);
-       struct rtc_device *rtc = rtc_data->rtc;
-       int ret, handled = IRQ_NONE;
-       unsigned int sr;
-
-       ret = regmap_read(rtc_data->regmap, ISL12057_REG_SR, &sr);
-       if (!ret && (sr & ISL12057_REG_SR_A1F)) {
-               dev_dbg(&client->dev, "RTC alarm!\n");
-
-               rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
-
-               /* Acknowledge and disable the alarm */
-               _isl12057_rtc_clear_alarm(&client->dev);
-               _isl12057_rtc_update_alarm(&client->dev, 0);
-
-               handled = IRQ_HANDLED;
-       }
-
-       return handled;
-}
-
-static const struct rtc_class_ops rtc_ops = {
-       .read_time = _isl12057_rtc_read_time,
-       .set_time = isl12057_rtc_set_time,
-       .read_alarm = isl12057_rtc_read_alarm,
-       .set_alarm = isl12057_rtc_set_alarm,
-       .alarm_irq_enable = isl12057_rtc_alarm_irq_enable,
-};
-
-static const struct regmap_config isl12057_rtc_regmap_config = {
-       .reg_bits = 8,
-       .val_bits = 8,
-};
-
-static int isl12057_probe(struct i2c_client *client,
-                         const struct i2c_device_id *id)
-{
-       struct device *dev = &client->dev;
-       struct isl12057_rtc_data *data;
-       struct regmap *regmap;
-       int ret;
-
-       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
-                                    I2C_FUNC_SMBUS_BYTE_DATA |
-                                    I2C_FUNC_SMBUS_I2C_BLOCK))
-               return -ENODEV;
-
-       regmap = devm_regmap_init_i2c(client, &isl12057_rtc_regmap_config);
-       if (IS_ERR(regmap)) {
-               ret = PTR_ERR(regmap);
-               dev_err(dev, "%s: regmap allocation failed (%d)\n",
-                       __func__, ret);
-               return ret;
-       }
-
-       ret = isl12057_i2c_validate_chip(regmap);
-       if (ret)
-               return ret;
-
-       ret = isl12057_check_rtc_status(dev, regmap);
-       if (ret)
-               return ret;
-
-       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       mutex_init(&data->lock);
-       data->regmap = regmap;
-       dev_set_drvdata(dev, data);
-
-       if (client->irq > 0) {
-               ret = devm_request_threaded_irq(dev, client->irq, NULL,
-                                               isl12057_rtc_interrupt,
-                                               IRQF_SHARED|IRQF_ONESHOT,
-                                               DRV_NAME, client);
-               if (!ret)
-                       data->irq = client->irq;
-               else
-                       dev_err(dev, "%s: irq %d unavailable (%d)\n", __func__,
-                               client->irq, ret);
-       }
-
-       if (isl12057_can_wakeup_machine(dev))
-               device_init_wakeup(dev, true);
-
-       data->rtc = devm_rtc_device_register(dev, DRV_NAME, &rtc_ops,
-                                            THIS_MODULE);
-       ret = PTR_ERR_OR_ZERO(data->rtc);
-       if (ret) {
-               dev_err(dev, "%s: unable to register RTC device (%d)\n",
-                       __func__, ret);
-               goto err;
-       }
-
-       /* We cannot support UIE mode if we do not have an IRQ line */
-       if (!data->irq)
-               data->rtc->uie_unsupported = 1;
-
-err:
-       return ret;
-}
-
-static int isl12057_remove(struct i2c_client *client)
-{
-       if (isl12057_can_wakeup_machine(&client->dev))
-               device_init_wakeup(&client->dev, false);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int isl12057_rtc_suspend(struct device *dev)
-{
-       struct isl12057_rtc_data *rtc_data = dev_get_drvdata(dev);
-
-       if (rtc_data->irq && device_may_wakeup(dev))
-               return enable_irq_wake(rtc_data->irq);
-
-       return 0;
-}
-
-static int isl12057_rtc_resume(struct device *dev)
-{
-       struct isl12057_rtc_data *rtc_data = dev_get_drvdata(dev);
-
-       if (rtc_data->irq && device_may_wakeup(dev))
-               return disable_irq_wake(rtc_data->irq);
-
-       return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(isl12057_rtc_pm_ops, isl12057_rtc_suspend,
-                        isl12057_rtc_resume);
-
-#ifdef CONFIG_OF
-static const struct of_device_id isl12057_dt_match[] = {
-       { .compatible = "isl,isl12057" }, /* for backward compat., don't use */
-       { .compatible = "isil,isl12057" },
-       { },
-};
-MODULE_DEVICE_TABLE(of, isl12057_dt_match);
-#endif
-
-static const struct i2c_device_id isl12057_id[] = {
-       { "isl12057", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, isl12057_id);
-
-static struct i2c_driver isl12057_driver = {
-       .driver = {
-               .name = DRV_NAME,
-               .pm = &isl12057_rtc_pm_ops,
-               .of_match_table = of_match_ptr(isl12057_dt_match),
-       },
-       .probe    = isl12057_probe,
-       .remove   = isl12057_remove,
-       .id_table = isl12057_id,
-};
-module_i2c_driver(isl12057_driver);
-
-MODULE_AUTHOR("Arnaud EBALARD <arno@natisbad.org>");
-MODULE_DESCRIPTION("Intersil ISL12057 RTC driver");
-MODULE_LICENSE("GPL");
index b2bcfc0bf2e51c6ee050d7705f6305bb7cd09148..5e14651b71a89a2327a2f1fe175cc56156d6a880 100644 (file)
@@ -174,7 +174,7 @@ static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
        return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable);
 }
 
-static struct rtc_class_ops jz4740_rtc_ops = {
+static const struct rtc_class_ops jz4740_rtc_ops = {
        .read_time      = jz4740_rtc_read_time,
        .set_mmss       = jz4740_rtc_set_mmss,
        .read_alarm     = jz4740_rtc_read_alarm,
index 025bb33b9cd2d0b9ae31499f33e92c55460e703a..4021fd04cb0ac847c955c4de79e283a97b73ff17 100644 (file)
@@ -151,7 +151,7 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim)
        return rtc_valid_tm(tim);
 }
 
-static struct rtc_class_ops mcp795_rtc_ops = {
+static const struct rtc_class_ops mcp795_rtc_ops = {
                .read_time = mcp795_read_time,
                .set_time = mcp795_set_time
 };
index 44f622c3e0488aaf4481b5accd5b734adc3a39fe..1a61fa56f3ad77bad999d234778e8d953fc1ad5a 100644 (file)
@@ -301,7 +301,7 @@ exit:
        return ret;
 }
 
-static struct rtc_class_ops mtk_rtc_ops = {
+static const struct rtc_class_ops mtk_rtc_ops = {
        .read_time  = mtk_rtc_read_time,
        .set_time   = mtk_rtc_set_time,
        .read_alarm = mtk_rtc_read_alarm,
index 09fc1c19f0dfdd4f32e783d19c5d83b37d4cf6a4..b1b6b3041bfbc94eac0a478daabd7549bc891163 100644 (file)
@@ -214,7 +214,7 @@ static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        return 0;
 }
 
-static struct rtc_class_ops nuc900_rtc_ops = {
+static const struct rtc_class_ops nuc900_rtc_ops = {
        .read_time = nuc900_rtc_read_time,
        .set_time = nuc900_rtc_set_time,
        .read_alarm = nuc900_rtc_read_alarm,
index ec2e9c5fb993c7023c9262af5c0b691905443bff..b04ea9b5ae67348c69210d29f3f138e7d5b649e3 100644 (file)
  * 2 of the License, or (at your option) any later version.
  */
 
-#include <linux/kernel.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/init.h>
-#include <linux/module.h>
+#include <linux/io.h>
 #include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/rtc.h>
-#include <linux/bcd.h>
-#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
-#include <linux/io.h>
-#include <linux/clk.h>
+#include <linux/rtc.h>
 
 /*
  * The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock
 
 /* OMAP_RTC_PMIC bit fields: */
 #define OMAP_RTC_PMIC_POWER_EN_EN      BIT(16)
+#define OMAP_RTC_PMIC_EXT_WKUP_EN(x)   BIT(x)
+#define OMAP_RTC_PMIC_EXT_WKUP_POL(x)  BIT(4 + x)
 
 /* OMAP_RTC_KICKER values */
 #define        KICK0_VALUE                     0x83e70b13
@@ -141,6 +147,7 @@ struct omap_rtc {
        bool is_pmic_controller;
        bool has_ext_clk;
        const struct omap_rtc_device_type *type;
+       struct pinctrl_dev *pctldev;
 };
 
 static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg)
@@ -469,7 +476,7 @@ static void omap_rtc_power_off(void)
        mdelay(2500);
 }
 
-static struct rtc_class_ops omap_rtc_ops = {
+static const struct rtc_class_ops omap_rtc_ops = {
        .read_time      = omap_rtc_read_time,
        .set_time       = omap_rtc_set_time,
        .read_alarm     = omap_rtc_read_alarm,
@@ -525,6 +532,139 @@ static const struct of_device_id omap_rtc_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, omap_rtc_of_match);
 
+static const struct pinctrl_pin_desc rtc_pins_desc[] = {
+       PINCTRL_PIN(0, "ext_wakeup0"),
+       PINCTRL_PIN(1, "ext_wakeup1"),
+       PINCTRL_PIN(2, "ext_wakeup2"),
+       PINCTRL_PIN(3, "ext_wakeup3"),
+};
+
+static int rtc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+       return 0;
+}
+
+static const char *rtc_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
+                                       unsigned int group)
+{
+       return NULL;
+}
+
+static const struct pinctrl_ops rtc_pinctrl_ops = {
+       .get_groups_count = rtc_pinctrl_get_groups_count,
+       .get_group_name = rtc_pinctrl_get_group_name,
+       .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+       .dt_free_map = pinconf_generic_dt_free_map,
+};
+
+enum rtc_pin_config_param {
+       PIN_CONFIG_ACTIVE_HIGH = PIN_CONFIG_END + 1,
+};
+
+static const struct pinconf_generic_params rtc_params[] = {
+       {"ti,active-high", PIN_CONFIG_ACTIVE_HIGH, 0},
+};
+
+#ifdef CONFIG_DEBUG_FS
+static const struct pin_config_item rtc_conf_items[ARRAY_SIZE(rtc_params)] = {
+       PCONFDUMP(PIN_CONFIG_ACTIVE_HIGH, "input active high", NULL, false),
+};
+#endif
+
+static int rtc_pinconf_get(struct pinctrl_dev *pctldev,
+                       unsigned int pin, unsigned long *config)
+{
+       struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev);
+       unsigned int param = pinconf_to_config_param(*config);
+       u32 val;
+       u16 arg = 0;
+
+       rtc->type->unlock(rtc);
+       val = rtc_readl(rtc, OMAP_RTC_PMIC_REG);
+       rtc->type->lock(rtc);
+
+       switch (param) {
+       case PIN_CONFIG_INPUT_ENABLE:
+               if (!(val & OMAP_RTC_PMIC_EXT_WKUP_EN(pin)))
+                       return -EINVAL;
+               break;
+       case PIN_CONFIG_ACTIVE_HIGH:
+               if (val & OMAP_RTC_PMIC_EXT_WKUP_POL(pin))
+                       return -EINVAL;
+               break;
+       default:
+               return -ENOTSUPP;
+       };
+
+       *config = pinconf_to_config_packed(param, arg);
+
+       return 0;
+}
+
+static int rtc_pinconf_set(struct pinctrl_dev *pctldev,
+                       unsigned int pin, unsigned long *configs,
+                       unsigned int num_configs)
+{
+       struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev);
+       u32 val;
+       unsigned int param;
+       u16 param_val;
+       int i;
+
+       rtc->type->unlock(rtc);
+       val = rtc_readl(rtc, OMAP_RTC_PMIC_REG);
+       rtc->type->lock(rtc);
+
+       /* active low by default */
+       val |= OMAP_RTC_PMIC_EXT_WKUP_POL(pin);
+
+       for (i = 0; i < num_configs; i++) {
+               param = pinconf_to_config_param(configs[i]);
+               param_val = pinconf_to_config_argument(configs[i]);
+
+               switch (param) {
+               case PIN_CONFIG_INPUT_ENABLE:
+                       if (param_val)
+                               val |= OMAP_RTC_PMIC_EXT_WKUP_EN(pin);
+                       else
+                               val &= ~OMAP_RTC_PMIC_EXT_WKUP_EN(pin);
+                       break;
+               case PIN_CONFIG_ACTIVE_HIGH:
+                       val &= ~OMAP_RTC_PMIC_EXT_WKUP_POL(pin);
+                       break;
+               default:
+                       dev_err(&rtc->rtc->dev, "Property %u not supported\n",
+                               param);
+                       return -ENOTSUPP;
+               }
+       }
+
+       rtc->type->unlock(rtc);
+       rtc_writel(rtc, OMAP_RTC_PMIC_REG, val);
+       rtc->type->lock(rtc);
+
+       return 0;
+}
+
+static const struct pinconf_ops rtc_pinconf_ops = {
+       .is_generic = true,
+       .pin_config_get = rtc_pinconf_get,
+       .pin_config_set = rtc_pinconf_set,
+};
+
+static struct pinctrl_desc rtc_pinctrl_desc = {
+       .pins = rtc_pins_desc,
+       .npins = ARRAY_SIZE(rtc_pins_desc),
+       .pctlops = &rtc_pinctrl_ops,
+       .confops = &rtc_pinconf_ops,
+       .custom_params = rtc_params,
+       .num_custom_params = ARRAY_SIZE(rtc_params),
+#ifdef CONFIG_DEBUG_FS
+       .custom_conf_items = rtc_conf_items,
+#endif
+       .owner = THIS_MODULE,
+};
+
 static int omap_rtc_probe(struct platform_device *pdev)
 {
        struct omap_rtc *rtc;
@@ -681,6 +821,15 @@ static int omap_rtc_probe(struct platform_device *pdev)
                }
        }
 
+       /* Support ext_wakeup pinconf */
+       rtc_pinctrl_desc.name = dev_name(&pdev->dev);
+
+       rtc->pctldev = pinctrl_register(&rtc_pinctrl_desc, &pdev->dev, rtc);
+       if (IS_ERR(rtc->pctldev)) {
+               dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
+               return PTR_ERR(rtc->pctldev);
+       }
+
        return 0;
 
 err:
@@ -724,6 +873,9 @@ static int __exit omap_rtc_remove(struct platform_device *pdev)
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
+       /* Remove ext_wakeup pinconf */
+       pinctrl_unregister(rtc->pctldev);
+
        return 0;
 }
 
index 6080e0edef6328aa7d221ef48cc69c564b526a7a..4bcfb88674d38b5d3343149708ed188e7d2c707e 100644 (file)
@@ -225,7 +225,7 @@ static irqreturn_t palmas_rtc_interrupt(int irq, void *context)
        return IRQ_HANDLED;
 }
 
-static struct rtc_class_ops palmas_rtc_ops = {
+static const struct rtc_class_ops palmas_rtc_ops = {
        .read_time      = palmas_rtc_read_time,
        .set_time       = palmas_rtc_set_time,
        .read_alarm     = palmas_rtc_read_alarm,
index b4478cc92b55dbd76731ea8ec6a4228677adcebb..8895f77726e8da5444afcd602dceff8f25a9b3fd 100644 (file)
@@ -182,7 +182,8 @@ static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr,
 }
 
 static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
-                            const char *buffer, size_t count) {
+                            const char *buffer, size_t count)
+{
        struct pcf2123_sysfs_reg *r;
        unsigned long reg;
        unsigned long val;
@@ -199,7 +200,7 @@ static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
        if (ret)
                return ret;
 
-       pcf2123_write_reg(dev, reg, val);
+       ret = pcf2123_write_reg(dev, reg, val);
        if (ret < 0)
                return -EIO;
        return count;
index e6b6911c8e0528b326e6a0e93050510f81735b52..00c31c91b245fb080b5312cc2d7499268d3b1a4b 100644 (file)
@@ -232,7 +232,7 @@ static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        return ret;
 }
 
-static struct rtc_class_ops pcf50633_rtc_ops = {
+static const struct rtc_class_ops pcf50633_rtc_ops = {
        .read_time              = pcf50633_rtc_read_time,
        .set_time               = pcf50633_rtc_set_time,
        .read_alarm             = pcf50633_rtc_read_alarm,
index 64e1e4578492ab029c54be1e265598f611acce6f..5cfb6df5c43032e294ccc5a610637ba6e049b27f 100644 (file)
@@ -400,7 +400,6 @@ static struct platform_driver pic32_rtc_driver = {
        .remove         = pic32_rtc_remove,
        .driver         = {
                .name   = "pic32-rtc",
-               .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(pic32_rtc_dt_ids),
        },
 };
index 9a2f6a95d5a7cf5757308c3ce6668e2f78d20c35..f9277e536f7e8754120da35b17581b9f1492c632 100644 (file)
 #define RV8803_CTRL_TIE                        BIT(4)
 #define RV8803_CTRL_UIE                        BIT(5)
 
+#define RX8900_BACKUP_CTRL             0x18
+#define RX8900_FLAG_SWOFF              BIT(2)
+#define RX8900_FLAG_VDETOFF            BIT(3)
+
+enum rv8803_type {
+       rv_8803,
+       rx_8900
+};
+
 struct rv8803_data {
        struct i2c_client *client;
        struct rtc_device *rtc;
        struct mutex flags_lock;
        u8 ctrl;
+       enum rv8803_type type;
 };
 
 static int rv8803_read_reg(const struct i2c_client *client, u8 reg)
@@ -497,6 +507,35 @@ static struct rtc_class_ops rv8803_rtc_ops = {
        .ioctl = rv8803_ioctl,
 };
 
+static int rx8900_trickle_charger_init(struct rv8803_data *rv8803)
+{
+       struct i2c_client *client = rv8803->client;
+       struct device_node *node = client->dev.of_node;
+       int err;
+       u8 flags;
+
+       if (!node)
+               return 0;
+
+       if (rv8803->type != rx_8900)
+               return 0;
+
+       err = i2c_smbus_read_byte_data(rv8803->client, RX8900_BACKUP_CTRL);
+       if (err < 0)
+               return err;
+
+       flags = ~(RX8900_FLAG_VDETOFF | RX8900_FLAG_SWOFF) & (u8)err;
+
+       if (of_property_read_bool(node, "epson,vdet-disable"))
+               flags |= RX8900_FLAG_VDETOFF;
+
+       if (of_property_read_bool(node, "trickle-diode-disable"))
+               flags |= RX8900_FLAG_SWOFF;
+
+       return i2c_smbus_write_byte_data(rv8803->client, RX8900_BACKUP_CTRL,
+                                        flags);
+}
+
 static int rv8803_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
@@ -517,6 +556,7 @@ static int rv8803_probe(struct i2c_client *client,
 
        mutex_init(&rv8803->flags_lock);
        rv8803->client = client;
+       rv8803->type = id->driver_data;
        i2c_set_clientdata(client, rv8803);
 
        flags = rv8803_read_reg(client, RV8803_FLAG);
@@ -558,6 +598,12 @@ static int rv8803_probe(struct i2c_client *client,
        if (err)
                return err;
 
+       err = rx8900_trickle_charger_init(rv8803);
+       if (err) {
+               dev_err(&client->dev, "failed to init charger\n");
+               return err;
+       }
+
        err = device_create_bin_file(&client->dev, &rv8803_nvram_attr);
        if (err)
                return err;
@@ -575,8 +621,8 @@ static int rv8803_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id rv8803_id[] = {
-       { "rv8803", 0 },
-       { "rx8900", 0 },
+       { "rv8803", rv_8803 },
+       { "rx8900", rx_8900 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, rv8803_id);
index bbad00b233bce1b2891ecc72ee13b60a16b714ef..7c9c08eab5e5b6baf52d0bafcf86d3d92698c693 100644 (file)
@@ -317,7 +317,7 @@ static int rx6110_init(struct rx6110_data *rx6110)
        return ret;
 }
 
-static struct rtc_class_ops rx6110_rtc_ops = {
+static const struct rtc_class_ops rx6110_rtc_ops = {
        .read_time = rx6110_get_time,
        .set_time = rx6110_set_time,
 };
@@ -388,7 +388,6 @@ MODULE_DEVICE_TABLE(spi, rx6110_id);
 static struct spi_driver rx6110_driver = {
        .driver = {
                .name = RX6110_DRIVER_NAME,
-               .owner = THIS_MODULE,
        },
        .probe          = rx6110_probe,
        .remove         = rx6110_remove,
index 2b85cc7a24e752c01d1cc16c60d738f7fbfc5983..91857d8d2df8707a75a0f931d5ba6afb6ed45afe 100644 (file)
@@ -403,7 +403,7 @@ static int rx8025_alarm_irq_enable(struct device *dev, unsigned int enabled)
        return 0;
 }
 
-static struct rtc_class_ops rx8025_rtc_ops = {
+static const struct rtc_class_ops rx8025_rtc_ops = {
        .read_time = rx8025_get_time,
        .set_time = rx8025_set_time,
        .read_alarm = rx8025_read_alarm,
index f05ef8568480429f912bf47f72349eb331d214ca..e377f42abae7ae952e2651aaa4805057d1d230c0 100644 (file)
@@ -343,7 +343,7 @@ static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled)
        return ret;
 }
 
-static struct rtc_class_ops spear_rtc_ops = {
+static const struct rtc_class_ops spear_rtc_ops = {
        .read_time = spear_rtc_read_time,
        .set_time = spear_rtc_set_time,
        .read_alarm = spear_rtc_read_alarm,
index e6aaaa52e7fe165383806a76ead6311df65e2433..d578e40d5a506308a76307a5050fd61552fc9420 100644 (file)
@@ -231,7 +231,7 @@ static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
        return 0;
 }
 
-static struct rtc_class_ops stmp3xxx_rtc_ops = {
+static const struct rtc_class_ops stmp3xxx_rtc_ops = {
        .alarm_irq_enable =
                          stmp3xxx_alarm_irq_enable,
        .read_time      = stmp3xxx_rtc_gettime,
index 63b9fb1318c2ae905db045458eb7e42ae4ae3338..1218d5d4224ddae32c28be3b4977a597120027f0 100644 (file)
@@ -160,7 +160,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
        unsigned long push = 0;
        struct rtc_wkalrm alm;
        struct rtc_device *rtc = to_rtc_device(dev);
-       char *buf_ptr;
+       const char *buf_ptr;
        int adjust = 0;
 
        /* Only request alarms that trigger in the future.  Disable them
@@ -171,7 +171,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
                return retval;
        rtc_tm_to_time(&alm.time, &now);
 
-       buf_ptr = (char *)buf;
+       buf_ptr = buf;
        if (*buf_ptr == '+') {
                buf_ptr++;
                if (*buf_ptr == '=') {
index 15ac597d54da20515847ced69d342ae3051f255e..3853ba963bb5d801502b8d50d992c83aeb886eb0 100644 (file)
@@ -291,7 +291,7 @@ static irqreturn_t tegra_rtc_irq_handler(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static struct rtc_class_ops tegra_rtc_ops = {
+static const struct rtc_class_ops tegra_rtc_ops = {
        .read_time      = tegra_rtc_read_time,
        .set_time       = tegra_rtc_set_time,
        .read_alarm     = tegra_rtc_read_alarm,
index 2dc787dc06c172a7cb7371a345e679467741a5de..176720b7b9e5083195b3bd789dbf5a8a622cd6c8 100644 (file)
@@ -462,7 +462,7 @@ out:
        return ret;
 }
 
-static struct rtc_class_ops twl_rtc_ops = {
+static const struct rtc_class_ops twl_rtc_ops = {
        .read_time      = twl_rtc_read_time,
        .set_time       = twl_rtc_set_time,
        .read_alarm     = twl_rtc_read_alarm,
index 6a6906f847dbba07ef38149a9912563d48ba3d82..68138a647dfc192c93f44c0ca801066a751217bb 100644 (file)
@@ -61,7 +61,7 @@ MODULE_PARM_DESC(be_max_phys_size,
                "memory that can be allocated. Range is 16 - 128");
 
 #define beiscsi_disp_param(_name)\
-ssize_t        \
+static ssize_t \
 beiscsi_##_name##_disp(struct device *dev,\
                        struct device_attribute *attrib, char *buf)     \
 {      \
@@ -74,7 +74,7 @@ beiscsi_##_name##_disp(struct device *dev,\
 }
 
 #define beiscsi_change_param(_name, _minval, _maxval, _defaval)\
-int \
+static int \
 beiscsi_##_name##_change(struct beiscsi_hba *phba, uint32_t val)\
 {\
        if (val >= _minval && val <= _maxval) {\
@@ -93,7 +93,7 @@ beiscsi_##_name##_change(struct beiscsi_hba *phba, uint32_t val)\
 }
 
 #define beiscsi_store_param(_name)  \
-ssize_t \
+static ssize_t \
 beiscsi_##_name##_store(struct device *dev,\
                         struct device_attribute *attr, const char *buf,\
                         size_t count) \
@@ -112,7 +112,7 @@ beiscsi_##_name##_store(struct device *dev,\
 }
 
 #define beiscsi_init_param(_name, _minval, _maxval, _defval) \
-int \
+static int \
 beiscsi_##_name##_init(struct beiscsi_hba *phba, uint32_t val) \
 { \
        if (val >= _minval && val <= _maxval) {\
@@ -4584,7 +4584,7 @@ free_hndls:
        io_task->cmd_bhs = NULL;
        return -ENOMEM;
 }
-int beiscsi_iotask_v2(struct iscsi_task *task, struct scatterlist *sg,
+static int beiscsi_iotask_v2(struct iscsi_task *task, struct scatterlist *sg,
                       unsigned int num_sg, unsigned int xferlen,
                       unsigned int writedir)
 {
@@ -4973,7 +4973,7 @@ static int beiscsi_bsg_request(struct bsg_job *job)
        return rc;
 }
 
-void beiscsi_hba_attrs_init(struct beiscsi_hba *phba)
+static void beiscsi_hba_attrs_init(struct beiscsi_hba *phba)
 {
        /* Set the logging parameter */
        beiscsi_log_enable_init(phba, beiscsi_log_enable);
index 7c0d7af0d3b7f3678e5040b7223dab051eb8eab8..0039bebaa9e24407bc00650fe0906a60f05247af 100644 (file)
@@ -685,6 +685,11 @@ static int push_tx_frames(struct cxgbi_sock *csk, int req_completion)
                                        req_completion);
                        csk->snd_nxt += len;
                        cxgbi_skcb_clear_flag(skb, SKCBF_TX_NEED_HDR);
+               } else if (cxgbi_skcb_test_flag(skb, SKCBF_TX_FLAG_COMPL) &&
+                          (csk->wr_una_cred >= (csk->wr_max_cred / 2))) {
+                       struct cpl_close_con_req *req =
+                               (struct cpl_close_con_req *)skb->data;
+                       req->wr.wr_hi |= htonl(FW_WR_COMPL_F);
                }
                total_size += skb->truesize;
                t4_set_arp_err_handler(skb, csk, arp_failure_skb_discard);
index 516bd6c4f4425036cd7a7bd84ab1d1a1ef3643c7..cbf010324c187589e199f2b14ef3793bf892f2a9 100644 (file)
 #include "NCR5380.h"
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/isapnp.h>
+#include <linux/isa.h>
+#include <linux/pnp.h>
 #include <linux/interrupt.h>
 
+#define MAX_CARDS 8
+
+/* old-style parameters for compatibility */
 static int ncr_irq;
-static int ncr_dma;
 static int ncr_addr;
 static int ncr_5380;
 static int ncr_53c400;
 static int ncr_53c400a;
 static int dtc_3181e;
 static int hp_c2502;
+module_param(ncr_irq, int, 0);
+module_param(ncr_addr, int, 0);
+module_param(ncr_5380, int, 0);
+module_param(ncr_53c400, int, 0);
+module_param(ncr_53c400a, int, 0);
+module_param(dtc_3181e, int, 0);
+module_param(hp_c2502, int, 0);
 
-static struct override {
-       NCR5380_map_type NCR5380_map_name;
-       int irq;
-       int dma;
-       int board;              /* Use NCR53c400, Ricoh, etc. extensions ? */
-} overrides
-#ifdef GENERIC_NCR5380_OVERRIDE
-[] __initdata = GENERIC_NCR5380_OVERRIDE;
-#else
-[1] __initdata = { { 0,},};
-#endif
-
-#define NO_OVERRIDES ARRAY_SIZE(overrides)
-
-#ifndef MODULE
-
-/**
- *     internal_setup          -       handle lilo command string override
- *     @board: BOARD_* identifier for the board
- *     @str: unused
- *     @ints: numeric parameters
- *
- *     Do LILO command line initialization of the overrides array. Display
- *     errors when needed
- *
- *     Locks: none
- */
-
-static void __init internal_setup(int board, char *str, int *ints)
-{
-       static int commandline_current;
-       switch (board) {
-       case BOARD_NCR5380:
-               if (ints[0] != 2 && ints[0] != 3) {
-                       printk(KERN_ERR "generic_NCR5380_setup : usage ncr5380=" STRVAL(NCR5380_map_name) ",irq,dma\n");
-                       return;
-               }
-               break;
-       case BOARD_NCR53C400:
-               if (ints[0] != 2) {
-                       printk(KERN_ERR "generic_NCR53C400_setup : usage ncr53c400=" STRVAL(NCR5380_map_name) ",irq\n");
-                       return;
-               }
-               break;
-       case BOARD_NCR53C400A:
-               if (ints[0] != 2) {
-                       printk(KERN_ERR "generic_NCR53C400A_setup : usage ncr53c400a=" STRVAL(NCR5380_map_name) ",irq\n");
-                       return;
-               }
-               break;
-       case BOARD_DTC3181E:
-               if (ints[0] != 2) {
-                       printk("generic_DTC3181E_setup : usage dtc3181e=" STRVAL(NCR5380_map_name) ",irq\n");
-                       return;
-               }
-               break;
-       }
-
-       if (commandline_current < NO_OVERRIDES) {
-               overrides[commandline_current].NCR5380_map_name = (NCR5380_map_type) ints[1];
-               overrides[commandline_current].irq = ints[2];
-               if (ints[0] == 3)
-                       overrides[commandline_current].dma = ints[3];
-               else
-                       overrides[commandline_current].dma = DMA_NONE;
-               overrides[commandline_current].board = board;
-               ++commandline_current;
-       }
-}
-
-
-/**
- *     do_NCR53C80_setup               -       set up entry point
- *     @str: unused
- *
- *     Setup function invoked at boot to parse the ncr5380= command
- *     line.
- */
-
-static int __init do_NCR5380_setup(char *str)
-{
-       int ints[10];
-
-       get_options(str, ARRAY_SIZE(ints), ints);
-       internal_setup(BOARD_NCR5380, str, ints);
-       return 1;
-}
-
-/**
- *     do_NCR53C400_setup              -       set up entry point
- *     @str: unused
- *     @ints: integer parameters from kernel setup code
- *
- *     Setup function invoked at boot to parse the ncr53c400= command
- *     line.
- */
-
-static int __init do_NCR53C400_setup(char *str)
-{
-       int ints[10];
-
-       get_options(str, ARRAY_SIZE(ints), ints);
-       internal_setup(BOARD_NCR53C400, str, ints);
-       return 1;
-}
-
-/**
- *     do_NCR53C400A_setup     -       set up entry point
- *     @str: unused
- *     @ints: integer parameters from kernel setup code
- *
- *     Setup function invoked at boot to parse the ncr53c400a= command
- *     line.
- */
-
-static int __init do_NCR53C400A_setup(char *str)
-{
-       int ints[10];
-
-       get_options(str, ARRAY_SIZE(ints), ints);
-       internal_setup(BOARD_NCR53C400A, str, ints);
-       return 1;
-}
-
-/**
- *     do_DTC3181E_setup       -       set up entry point
- *     @str: unused
- *     @ints: integer parameters from kernel setup code
- *
- *     Setup function invoked at boot to parse the dtc3181e= command
- *     line.
- */
+static int irq[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(irq, "IRQ number(s)");
 
-static int __init do_DTC3181E_setup(char *str)
-{
-       int ints[10];
+static int base[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+module_param_array(base, int, NULL, 0);
+MODULE_PARM_DESC(base, "base address(es)");
 
-       get_options(str, ARRAY_SIZE(ints), ints);
-       internal_setup(BOARD_DTC3181E, str, ints);
-       return 1;
-}
+static int card[] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+module_param_array(card, int, NULL, 0);
+MODULE_PARM_DESC(card, "card type (0=NCR5380, 1=NCR53C400, 2=NCR53C400A, 3=DTC3181E, 4=HP C2502)");
 
-#endif
+MODULE_LICENSE("GPL");
 
 #ifndef SCSI_G_NCR5380_MEM
 /*
@@ -210,21 +90,9 @@ static void magic_configure(int idx, u8 irq, u8 magic[])
 }
 #endif
 
-/**
- *     generic_NCR5380_detect  -       look for NCR5380 controllers
- *     @tpnt: the scsi template
- *
- *     Scan for the present of NCR5380, NCR53C400, NCR53C400A, DTC3181E
- *     and DTC436(ISAPnP) controllers. If overrides have been set we use
- *     them.
- *
- *     Locks: none
- */
-
-static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt)
+static int generic_NCR5380_init_one(struct scsi_host_template *tpnt,
+                       struct device *pdev, int base, int irq, int board)
 {
-       static int current_override;
-       int count;
        unsigned int *ports;
        u8 *magic = NULL;
 #ifndef SCSI_G_NCR5380_MEM
@@ -232,272 +100,222 @@ static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt)
        int port_idx = -1;
        unsigned long region_size;
 #endif
-       static unsigned int __initdata ncr_53c400a_ports[] = {
+       static unsigned int ncr_53c400a_ports[] = {
                0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0
        };
-       static unsigned int __initdata dtc_3181e_ports[] = {
+       static unsigned int dtc_3181e_ports[] = {
                0x220, 0x240, 0x280, 0x2a0, 0x2c0, 0x300, 0x320, 0x340, 0
        };
-       static u8 ncr_53c400a_magic[] __initdata = {    /* 53C400A & DTC436 */
+       static u8 ncr_53c400a_magic[] = {       /* 53C400A & DTC436 */
                0x59, 0xb9, 0xc5, 0xae, 0xa6
        };
-       static u8 hp_c2502_magic[] __initdata = {       /* HP C2502 */
+       static u8 hp_c2502_magic[] = {  /* HP C2502 */
                0x0f, 0x22, 0xf0, 0x20, 0x80
        };
-       int flags;
+       int flags, ret;
        struct Scsi_Host *instance;
        struct NCR5380_hostdata *hostdata;
 #ifdef SCSI_G_NCR5380_MEM
-       unsigned long base;
        void __iomem *iomem;
        resource_size_t iomem_size;
 #endif
 
-       if (ncr_irq)
-               overrides[0].irq = ncr_irq;
-       if (ncr_dma)
-               overrides[0].dma = ncr_dma;
-       if (ncr_addr)
-               overrides[0].NCR5380_map_name = (NCR5380_map_type) ncr_addr;
-       if (ncr_5380)
-               overrides[0].board = BOARD_NCR5380;
-       else if (ncr_53c400)
-               overrides[0].board = BOARD_NCR53C400;
-       else if (ncr_53c400a)
-               overrides[0].board = BOARD_NCR53C400A;
-       else if (dtc_3181e)
-               overrides[0].board = BOARD_DTC3181E;
-       else if (hp_c2502)
-               overrides[0].board = BOARD_HP_C2502;
-#ifndef SCSI_G_NCR5380_MEM
-       if (!current_override && isapnp_present()) {
-               struct pnp_dev *dev = NULL;
-               count = 0;
-               while ((dev = pnp_find_dev(NULL, ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), dev))) {
-                       if (count >= NO_OVERRIDES)
-                               break;
-                       if (pnp_device_attach(dev) < 0)
-                               continue;
-                       if (pnp_activate_dev(dev) < 0) {
-                               printk(KERN_ERR "dtc436e probe: activate failed\n");
-                               pnp_device_detach(dev);
-                               continue;
-                       }
-                       if (!pnp_port_valid(dev, 0)) {
-                               printk(KERN_ERR "dtc436e probe: no valid port\n");
-                               pnp_device_detach(dev);
-                               continue;
-                       }
-                       if (pnp_irq_valid(dev, 0))
-                               overrides[count].irq = pnp_irq(dev, 0);
-                       else
-                               overrides[count].irq = NO_IRQ;
-                       if (pnp_dma_valid(dev, 0))
-                               overrides[count].dma = pnp_dma(dev, 0);
-                       else
-                               overrides[count].dma = DMA_NONE;
-                       overrides[count].NCR5380_map_name = (NCR5380_map_type) pnp_port_start(dev, 0);
-                       overrides[count].board = BOARD_DTC3181E;
-                       count++;
-               }
+       ports = NULL;
+       flags = 0;
+       switch (board) {
+       case BOARD_NCR5380:
+               flags = FLAG_NO_PSEUDO_DMA | FLAG_DMA_FIXUP;
+               break;
+       case BOARD_NCR53C400A:
+               ports = ncr_53c400a_ports;
+               magic = ncr_53c400a_magic;
+               break;
+       case BOARD_HP_C2502:
+               ports = ncr_53c400a_ports;
+               magic = hp_c2502_magic;
+               break;
+       case BOARD_DTC3181E:
+               ports = dtc_3181e_ports;
+               magic = ncr_53c400a_magic;
+               break;
        }
-#endif
-
-       for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
-               if (!(overrides[current_override].NCR5380_map_name))
-                       continue;
-
-               ports = NULL;
-               flags = 0;
-               switch (overrides[current_override].board) {
-               case BOARD_NCR5380:
-                       flags = FLAG_NO_PSEUDO_DMA | FLAG_DMA_FIXUP;
-                       break;
-               case BOARD_NCR53C400A:
-                       ports = ncr_53c400a_ports;
-                       magic = ncr_53c400a_magic;
-                       break;
-               case BOARD_HP_C2502:
-                       ports = ncr_53c400a_ports;
-                       magic = hp_c2502_magic;
-                       break;
-               case BOARD_DTC3181E:
-                       ports = dtc_3181e_ports;
-                       magic = ncr_53c400a_magic;
-                       break;
-               }
 
 #ifndef SCSI_G_NCR5380_MEM
-               if (ports && magic) {
-                       /* wakeup sequence for the NCR53C400A and DTC3181E */
-
-                       /* Disable the adapter and look for a free io port */
-                       magic_configure(-1, 0, magic);
-
-                       region_size = 16;
-
-                       if (overrides[current_override].NCR5380_map_name != PORT_AUTO)
-                               for (i = 0; ports[i]; i++) {
-                                       if (!request_region(ports[i], region_size, "ncr53c80"))
-                                               continue;
-                                       if (overrides[current_override].NCR5380_map_name == ports[i])
-                                               break;
-                                       release_region(ports[i], region_size);
-                       } else
-                               for (i = 0; ports[i]; i++) {
-                                       if (!request_region(ports[i], region_size, "ncr53c80"))
-                                               continue;
-                                       if (inb(ports[i]) == 0xff)
-                                               break;
-                                       release_region(ports[i], region_size);
+       if (ports && magic) {
+               /* wakeup sequence for the NCR53C400A and DTC3181E */
+
+               /* Disable the adapter and look for a free io port */
+               magic_configure(-1, 0, magic);
+
+               region_size = 16;
+               if (base)
+                       for (i = 0; ports[i]; i++) {
+                               if (base == ports[i]) { /* index found */
+                                       if (!request_region(ports[i],
+                                                           region_size,
+                                                           "ncr53c80"))
+                                               return -EBUSY;
+                                       break;
                                }
-                       if (ports[i]) {
-                               /* At this point we have our region reserved */
-                               magic_configure(i, 0, magic); /* no IRQ yet */
-                               outb(0xc0, ports[i] + 9);
-                               if (inb(ports[i] + 9) != 0x80)
-                                       continue;
-                               overrides[current_override].NCR5380_map_name = ports[i];
-                               port_idx = i;
-                       } else
-                               continue;
-               }
+                       }
                else
-               {
-                       /* Not a 53C400A style setup - just grab */
-                       region_size = 8;
-                       if (!request_region(overrides[current_override].NCR5380_map_name,
-                                           region_size, "ncr5380"))
-                               continue;
-               }
+                       for (i = 0; ports[i]; i++) {
+                               if (!request_region(ports[i], region_size,
+                                                   "ncr53c80"))
+                                       continue;
+                               if (inb(ports[i]) == 0xff)
+                                       break;
+                               release_region(ports[i], region_size);
+                       }
+               if (ports[i]) {
+                       /* At this point we have our region reserved */
+                       magic_configure(i, 0, magic); /* no IRQ yet */
+                       outb(0xc0, ports[i] + 9);
+                       if (inb(ports[i] + 9) != 0x80) {
+                               ret = -ENODEV;
+                               goto out_release;
+                       }
+                       base = ports[i];
+                       port_idx = i;
+               } else
+                       return -EINVAL;
+       }
+       else
+       {
+               /* NCR5380 - no configuration, just grab */
+               region_size = 8;
+               if (!base || !request_region(base, region_size, "ncr5380"))
+                       return -EBUSY;
+       }
 #else
-               base = overrides[current_override].NCR5380_map_name;
-               iomem_size = NCR53C400_region_size;
-               if (!request_mem_region(base, iomem_size, "ncr5380"))
-                       continue;
-               iomem = ioremap(base, iomem_size);
-               if (!iomem) {
-                       release_mem_region(base, iomem_size);
-                       continue;
-               }
+       iomem_size = NCR53C400_region_size;
+       if (!request_mem_region(base, iomem_size, "ncr5380"))
+               return -EBUSY;
+       iomem = ioremap(base, iomem_size);
+       if (!iomem) {
+               release_mem_region(base, iomem_size);
+               return -ENOMEM;
+       }
 #endif
-               instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata));
-               if (instance == NULL)
-                       goto out_release;
-               hostdata = shost_priv(instance);
+       instance = scsi_host_alloc(tpnt, sizeof(struct NCR5380_hostdata));
+       if (instance == NULL) {
+               ret = -ENOMEM;
+               goto out_release;
+       }
+       hostdata = shost_priv(instance);
 
 #ifndef SCSI_G_NCR5380_MEM
-               instance->io_port = overrides[current_override].NCR5380_map_name;
-               instance->n_io_port = region_size;
-               hostdata->io_width = 1; /* 8-bit PDMA by default */
-
-               /*
-                * On NCR53C400 boards, NCR5380 registers are mapped 8 past
-                * the base address.
-                */
-               switch (overrides[current_override].board) {
-               case BOARD_NCR53C400:
-                       instance->io_port += 8;
-                       hostdata->c400_ctl_status = 0;
-                       hostdata->c400_blk_cnt = 1;
-                       hostdata->c400_host_buf = 4;
-                       break;
-               case BOARD_DTC3181E:
-                       hostdata->io_width = 2; /* 16-bit PDMA */
-                       /* fall through */
-               case BOARD_NCR53C400A:
-               case BOARD_HP_C2502:
-                       hostdata->c400_ctl_status = 9;
-                       hostdata->c400_blk_cnt = 10;
-                       hostdata->c400_host_buf = 8;
-                       break;
-               }
+       instance->io_port = base;
+       instance->n_io_port = region_size;
+       hostdata->io_width = 1; /* 8-bit PDMA by default */
+
+       /*
+        * On NCR53C400 boards, NCR5380 registers are mapped 8 past
+        * the base address.
+        */
+       switch (board) {
+       case BOARD_NCR53C400:
+               instance->io_port += 8;
+               hostdata->c400_ctl_status = 0;
+               hostdata->c400_blk_cnt = 1;
+               hostdata->c400_host_buf = 4;
+               break;
+       case BOARD_DTC3181E:
+               hostdata->io_width = 2; /* 16-bit PDMA */
+               /* fall through */
+       case BOARD_NCR53C400A:
+       case BOARD_HP_C2502:
+               hostdata->c400_ctl_status = 9;
+               hostdata->c400_blk_cnt = 10;
+               hostdata->c400_host_buf = 8;
+               break;
+       }
 #else
-               instance->base = overrides[current_override].NCR5380_map_name;
-               hostdata->iomem = iomem;
-               hostdata->iomem_size = iomem_size;
-               switch (overrides[current_override].board) {
-               case BOARD_NCR53C400:
-                       hostdata->c400_ctl_status = 0x100;
-                       hostdata->c400_blk_cnt = 0x101;
-                       hostdata->c400_host_buf = 0x104;
-                       break;
-               case BOARD_DTC3181E:
-               case BOARD_NCR53C400A:
-               case BOARD_HP_C2502:
-                       pr_err(DRV_MODULE_NAME ": unknown register offsets\n");
-                       goto out_unregister;
-               }
+       instance->base = base;
+       hostdata->iomem = iomem;
+       hostdata->iomem_size = iomem_size;
+       switch (board) {
+       case BOARD_NCR53C400:
+               hostdata->c400_ctl_status = 0x100;
+               hostdata->c400_blk_cnt = 0x101;
+               hostdata->c400_host_buf = 0x104;
+               break;
+       case BOARD_DTC3181E:
+       case BOARD_NCR53C400A:
+       case BOARD_HP_C2502:
+               pr_err(DRV_MODULE_NAME ": unknown register offsets\n");
+               ret = -EINVAL;
+               goto out_unregister;
+       }
 #endif
 
-               if (NCR5380_init(instance, flags | FLAG_LATE_DMA_SETUP))
-                       goto out_unregister;
+       ret = NCR5380_init(instance, flags | FLAG_LATE_DMA_SETUP);
+       if (ret)
+               goto out_unregister;
 
-               switch (overrides[current_override].board) {
-               case BOARD_NCR53C400:
-               case BOARD_DTC3181E:
-               case BOARD_NCR53C400A:
-               case BOARD_HP_C2502:
-                       NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
-               }
+       switch (board) {
+       case BOARD_NCR53C400:
+       case BOARD_DTC3181E:
+       case BOARD_NCR53C400A:
+       case BOARD_HP_C2502:
+               NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
+       }
 
-               NCR5380_maybe_reset_bus(instance);
+       NCR5380_maybe_reset_bus(instance);
 
-               if (overrides[current_override].irq != IRQ_AUTO)
-                       instance->irq = overrides[current_override].irq;
-               else
-                       instance->irq = NCR5380_probe_irq(instance, 0xffff);
+       if (irq != IRQ_AUTO)
+               instance->irq = irq;
+       else
+               instance->irq = NCR5380_probe_irq(instance, 0xffff);
 
-               /* Compatibility with documented NCR5380 kernel parameters */
-               if (instance->irq == 255)
-                       instance->irq = NO_IRQ;
+       /* Compatibility with documented NCR5380 kernel parameters */
+       if (instance->irq == 255)
+               instance->irq = NO_IRQ;
 
-               if (instance->irq != NO_IRQ) {
+       if (instance->irq != NO_IRQ) {
 #ifndef SCSI_G_NCR5380_MEM
-                       /* set IRQ for HP C2502 */
-                       if (overrides[current_override].board == BOARD_HP_C2502)
-                               magic_configure(port_idx, instance->irq, magic);
+               /* set IRQ for HP C2502 */
+               if (board == BOARD_HP_C2502)
+                       magic_configure(port_idx, instance->irq, magic);
 #endif
-                       if (request_irq(instance->irq, generic_NCR5380_intr,
-                                       0, "NCR5380", instance)) {
-                               printk(KERN_WARNING "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
-                               instance->irq = NO_IRQ;
-                       }
-               }
-
-               if (instance->irq == NO_IRQ) {
-                       printk(KERN_INFO "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
-                       printk(KERN_INFO "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+               if (request_irq(instance->irq, generic_NCR5380_intr,
+                               0, "NCR5380", instance)) {
+                       printk(KERN_WARNING "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
+                       instance->irq = NO_IRQ;
                }
+       }
 
-               ++current_override;
-               ++count;
+       if (instance->irq == NO_IRQ) {
+               printk(KERN_INFO "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+               printk(KERN_INFO "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
        }
-       return count;
 
+       ret = scsi_add_host(instance, pdev);
+       if (ret)
+               goto out_free_irq;
+       scsi_scan_host(instance);
+       dev_set_drvdata(pdev, instance);
+       return 0;
+
+out_free_irq:
+       if (instance->irq != NO_IRQ)
+               free_irq(instance->irq, instance);
+       NCR5380_exit(instance);
 out_unregister:
-       scsi_unregister(instance);
+       scsi_host_put(instance);
 out_release:
 #ifndef SCSI_G_NCR5380_MEM
-       release_region(overrides[current_override].NCR5380_map_name, region_size);
+       release_region(base, region_size);
 #else
        iounmap(iomem);
        release_mem_region(base, iomem_size);
 #endif
-       return count;
+       return ret;
 }
 
-/**
- *     generic_NCR5380_release_resources       -       free resources
- *     @instance: host adapter to clean up 
- *
- *     Free the generic interface resources from this adapter.
- *
- *     Locks: none
- */
-static int generic_NCR5380_release_resources(struct Scsi_Host *instance)
+static void generic_NCR5380_release_resources(struct Scsi_Host *instance)
 {
+       scsi_remove_host(instance);
        if (instance->irq != NO_IRQ)
                free_irq(instance->irq, instance);
        NCR5380_exit(instance);
@@ -511,7 +329,7 @@ static int generic_NCR5380_release_resources(struct Scsi_Host *instance)
                release_mem_region(instance->base, hostdata->iomem_size);
        }
 #endif
-       return 0;
+       scsi_host_put(instance);
 }
 
 /**
@@ -701,10 +519,9 @@ static int generic_NCR5380_dma_xfer_len(struct Scsi_Host *instance,
 #include "NCR5380.c"
 
 static struct scsi_host_template driver_template = {
+       .module                 = THIS_MODULE,
        .proc_name              = DRV_MODULE_NAME,
        .name                   = "Generic NCR5380/NCR53C400 SCSI",
-       .detect                 = generic_NCR5380_detect,
-       .release                = generic_NCR5380_release_resources,
        .info                   = generic_NCR5380_info,
        .queuecommand           = generic_NCR5380_queue_command,
        .eh_abort_handler       = generic_NCR5380_abort,
@@ -718,31 +535,115 @@ static struct scsi_host_template driver_template = {
        .max_sectors            = 128,
 };
 
-#include "scsi_module.c"
 
-module_param(ncr_irq, int, 0);
-module_param(ncr_dma, int, 0);
-module_param(ncr_addr, int, 0);
-module_param(ncr_5380, int, 0);
-module_param(ncr_53c400, int, 0);
-module_param(ncr_53c400a, int, 0);
-module_param(dtc_3181e, int, 0);
-module_param(hp_c2502, int, 0);
-MODULE_LICENSE("GPL");
+static int generic_NCR5380_isa_match(struct device *pdev, unsigned int ndev)
+{
+       int ret = generic_NCR5380_init_one(&driver_template, pdev, base[ndev],
+                                         irq[ndev], card[ndev]);
+       if (ret) {
+               if (base[ndev])
+                       printk(KERN_WARNING "Card not found at address 0x%03x\n",
+                              base[ndev]);
+               return 0;
+       }
 
-#if !defined(SCSI_G_NCR5380_MEM) && defined(MODULE)
-static struct isapnp_device_id id_table[] = {
-       {
-        ISAPNP_ANY_ID, ISAPNP_ANY_ID,
-        ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e),
-        0},
-       {0}
+       return 1;
+}
+
+static int generic_NCR5380_isa_remove(struct device *pdev,
+                                  unsigned int ndev)
+{
+       generic_NCR5380_release_resources(dev_get_drvdata(pdev));
+       dev_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+static struct isa_driver generic_NCR5380_isa_driver = {
+       .match          = generic_NCR5380_isa_match,
+       .remove         = generic_NCR5380_isa_remove,
+       .driver         = {
+               .name   = DRV_MODULE_NAME
+       },
+};
+
+#if !defined(SCSI_G_NCR5380_MEM) && defined(CONFIG_PNP)
+static struct pnp_device_id generic_NCR5380_pnp_ids[] = {
+       { .id = "DTC436e", .driver_data = BOARD_DTC3181E },
+       { .id = "" }
+};
+MODULE_DEVICE_TABLE(pnp, generic_NCR5380_pnp_ids);
+
+static int generic_NCR5380_pnp_probe(struct pnp_dev *pdev,
+                              const struct pnp_device_id *id)
+{
+       int base, irq;
+
+       if (pnp_activate_dev(pdev) < 0)
+               return -EBUSY;
+
+       base = pnp_port_start(pdev, 0);
+       irq = pnp_irq(pdev, 0);
+
+       return generic_NCR5380_init_one(&driver_template, &pdev->dev, base, irq,
+                                      id->driver_data);
+}
+
+static void generic_NCR5380_pnp_remove(struct pnp_dev *pdev)
+{
+       generic_NCR5380_release_resources(pnp_get_drvdata(pdev));
+       pnp_set_drvdata(pdev, NULL);
+}
+
+static struct pnp_driver generic_NCR5380_pnp_driver = {
+       .name           = DRV_MODULE_NAME,
+       .id_table       = generic_NCR5380_pnp_ids,
+       .probe          = generic_NCR5380_pnp_probe,
+       .remove         = generic_NCR5380_pnp_remove,
 };
+#endif /* !defined(SCSI_G_NCR5380_MEM) && defined(CONFIG_PNP) */
+
+static int pnp_registered, isa_registered;
+
+static int __init generic_NCR5380_init(void)
+{
+       int ret = 0;
+
+       /* compatibility with old-style parameters */
+       if (irq[0] == 0 && base[0] == 0 && card[0] == -1) {
+               irq[0] = ncr_irq;
+               base[0] = ncr_addr;
+               if (ncr_5380)
+                       card[0] = BOARD_NCR5380;
+               if (ncr_53c400)
+                       card[0] = BOARD_NCR53C400;
+               if (ncr_53c400a)
+                       card[0] = BOARD_NCR53C400A;
+               if (dtc_3181e)
+                       card[0] = BOARD_DTC3181E;
+               if (hp_c2502)
+                       card[0] = BOARD_HP_C2502;
+       }
 
-MODULE_DEVICE_TABLE(isapnp, id_table);
+#if !defined(SCSI_G_NCR5380_MEM) && defined(CONFIG_PNP)
+       if (!pnp_register_driver(&generic_NCR5380_pnp_driver))
+               pnp_registered = 1;
 #endif
+       ret = isa_register_driver(&generic_NCR5380_isa_driver, MAX_CARDS);
+       if (!ret)
+               isa_registered = 1;
+
+       return (pnp_registered || isa_registered) ? 0 : ret;
+}
+
+static void __exit generic_NCR5380_exit(void)
+{
+#if !defined(SCSI_G_NCR5380_MEM) && defined(CONFIG_PNP)
+       if (pnp_registered)
+               pnp_unregister_driver(&generic_NCR5380_pnp_driver);
+#endif
+       if (isa_registered)
+               isa_unregister_driver(&generic_NCR5380_isa_driver);
+}
 
-__setup("ncr5380=", do_NCR5380_setup);
-__setup("ncr53c400=", do_NCR53C400_setup);
-__setup("ncr53c400a=", do_NCR53C400A_setup);
-__setup("dtc3181e=", do_DTC3181E_setup);
+module_init(generic_NCR5380_init);
+module_exit(generic_NCR5380_exit);
index 595177428d7628f492f37b04413d449884b83f7c..b175b92344586aee3a1247e85288f34731fe179c 100644 (file)
 #ifndef GENERIC_NCR5380_H
 #define GENERIC_NCR5380_H
 
-#define __STRVAL(x) #x
-#define STRVAL(x) __STRVAL(x)
-
 #ifndef SCSI_G_NCR5380_MEM
 #define DRV_MODULE_NAME "g_NCR5380"
 
-#define NCR5380_map_type int
-#define NCR5380_map_name port
-
 #define NCR5380_read(reg) \
        inb(instance->io_port + (reg))
 #define NCR5380_write(reg, value) \
@@ -38,8 +32,6 @@
 /* therefore SCSI_G_NCR5380_MEM */
 #define DRV_MODULE_NAME "g_NCR5380_mmio"
 
-#define NCR5380_map_type unsigned long
-#define NCR5380_map_name base
 #define NCR53C400_mem_base 0x3880
 #define NCR53C400_host_buffer 0x3900
 #define NCR53C400_region_size 0x3a00
index 7af5226aa55ba0937b69b8b0f2d5d070f3eccf7d..618422ea3a4123d8d0b115f8811634e8225de8a1 100644 (file)
@@ -4922,9 +4922,8 @@ static int sgl_map_user_pages(struct st_buffer *STbp,
        res = get_user_pages_unlocked(
                uaddr,
                nr_pages,
-               rw == READ,
-               0, /* don't force */
-               pages);
+               pages,
+               rw == READ ? FOLL_WRITE : 0); /* don't force */
 
        /* Errors and no page mapped should return here */
        if (res < nr_pages)
index 47966909286dcbd19f8837d1e25d5bcab928737a..e27b4d4e6ae2d2d564b2777335fa625fd92ddcb7 100644 (file)
@@ -63,7 +63,7 @@ config SCSI_UFSHCD_PCI
 
 config SCSI_UFS_DWC_TC_PCI
        tristate "DesignWare pci support using a G210 Test Chip"
-       depends on SCSI_UFSHCD && PCI
+       depends on SCSI_UFSHCD_PCI
        ---help---
          Synopsys Test Chip is a PHY for prototyping purposes.
 
index ee4ab85e2801be3a233d6e479e1c506640bca384..22f881e9253a256a1614df77048081cafc12efdb 100644 (file)
@@ -25,6 +25,7 @@
 
 #define UFS_VENDOR_TOSHIBA     0x198
 #define UFS_VENDOR_SAMSUNG     0x1CE
+#define UFS_VENDOR_SKHYNIX     0x1AD
 
 /**
  * ufs_device_info - ufs device details
@@ -145,6 +146,7 @@ static struct ufs_dev_fix ufs_fixups[] = {
                UFS_DEVICE_QUIRK_PA_TACTIVATE),
        UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
                UFS_DEVICE_QUIRK_PA_TACTIVATE),
+       UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
 
        END_FIX
 };
index 37f3c51e9d92166365053bf288701a64b06d051a..05c745663c103a7ddb70d4e6845702a6c08ebd08 100644 (file)
@@ -1266,9 +1266,12 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba,
        ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
                        0, query->request.query_func, 0, 0);
 
-       /* Data segment length */
-       ucd_req_ptr->header.dword_2 = UPIU_HEADER_DWORD(
-                       0, 0, len >> 8, (u8)len);
+       /* Data segment length only need for WRITE_DESC */
+       if (query->request.upiu_req.opcode == UPIU_QUERY_OPCODE_WRITE_DESC)
+               ucd_req_ptr->header.dword_2 =
+                       UPIU_HEADER_DWORD(0, 0, (len >> 8), (u8)len);
+       else
+               ucd_req_ptr->header.dword_2 = 0;
 
        /* Copy the Query Request buffer as is */
        memcpy(&ucd_req_ptr->qr, &query->request.upiu_req,
@@ -6500,6 +6503,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
                if (IS_ERR(hba->devfreq)) {
                        dev_err(hba->dev, "Unable to register with devfreq %ld\n",
                                        PTR_ERR(hba->devfreq));
+                       err = PTR_ERR(hba->devfreq);
                        goto out_remove_scsi_host;
                }
                /* Suspend devfreq until the UFS device is detected */
index fe42a2fdf351c7af04f785eb1e717b2c29e551a1..e6e90e80519a7db028fb23dcc6f121b1c64fc0e6 100644 (file)
@@ -1,6 +1,7 @@
 menu "SOC (System On Chip) specific Drivers"
 
 source "drivers/soc/bcm/Kconfig"
+source "drivers/soc/fsl/qbman/Kconfig"
 source "drivers/soc/fsl/qe/Kconfig"
 source "drivers/soc/mediatek/Kconfig"
 source "drivers/soc/qcom/Kconfig"
index 203307fd92c15c37e4192c6eeaedb9f3bc8ddd23..75e1f5334821080e0643b47ae1fd592dc7124509 100644 (file)
@@ -2,5 +2,6 @@
 # Makefile for the Linux Kernel SOC fsl specific device drivers
 #
 
+obj-$(CONFIG_FSL_DPAA)                 += qbman/
 obj-$(CONFIG_QUICC_ENGINE)             += qe/
 obj-$(CONFIG_CPM)                      += qe/
diff --git a/drivers/soc/fsl/qbman/Kconfig b/drivers/soc/fsl/qbman/Kconfig
new file mode 100644 (file)
index 0000000..757033c
--- /dev/null
@@ -0,0 +1,67 @@
+menuconfig FSL_DPAA
+       bool "Freescale DPAA 1.x support"
+       depends on FSL_SOC_BOOKE
+       select GENERIC_ALLOCATOR
+       help
+         The Freescale Data Path Acceleration Architecture (DPAA) is a set of
+         hardware components on specific QorIQ multicore processors.
+         This architecture provides the infrastructure to support simplified
+         sharing of networking interfaces and accelerators by multiple CPUs.
+         The major h/w blocks composing DPAA are BMan and QMan.
+
+         The Buffer Manager (BMan) is a hardware buffer pool management block
+         that allows software and accelerators on the datapath to acquire and
+         release buffers in order to build frames.
+
+         The Queue Manager (QMan) is a hardware queue management block
+         that allows software and accelerators on the datapath to enqueue and
+         dequeue frames in order to communicate.
+
+if FSL_DPAA
+
+config FSL_DPAA_CHECKING
+       bool "Additional driver checking"
+       help
+         Compiles in additional checks, to sanity-check the drivers and
+         any use of the exported API. Not recommended for performance.
+
+config FSL_BMAN_TEST
+       tristate "BMan self-tests"
+       help
+         Compile the BMan self-test code. These tests will
+         exercise the BMan APIs to confirm functionality
+         of both the software drivers and hardware device.
+
+config FSL_BMAN_TEST_API
+       bool "High-level API self-test"
+       depends on FSL_BMAN_TEST
+       default y
+       help
+         This requires the presence of cpu-affine portals, and performs
+         high-level API testing with them (whichever portal(s) are affine
+         to the cpu(s) the test executes on).
+
+config FSL_QMAN_TEST
+       tristate "QMan self-tests"
+       help
+         Compile self-test code for QMan.
+
+config FSL_QMAN_TEST_API
+       bool "QMan high-level self-test"
+       depends on FSL_QMAN_TEST
+       default y
+       help
+         This requires the presence of cpu-affine portals, and performs
+         high-level API testing with them (whichever portal(s) are affine to
+         the cpu(s) the test executes on).
+
+config FSL_QMAN_TEST_STASH
+       bool "QMan 'hot potato' data-stashing self-test"
+       depends on FSL_QMAN_TEST
+       default y
+       help
+         This performs a "hot potato" style test enqueuing/dequeuing a frame
+         across a series of FQs scheduled to different portals (and cpus), with
+         DQRR, data and context stashing always on.
+
+endif # FSL_DPAA
diff --git a/drivers/soc/fsl/qbman/Makefile b/drivers/soc/fsl/qbman/Makefile
new file mode 100644 (file)
index 0000000..7ae199f
--- /dev/null
@@ -0,0 +1,12 @@
+obj-$(CONFIG_FSL_DPAA)                          += bman_ccsr.o qman_ccsr.o \
+                                                  bman_portal.o qman_portal.o \
+                                                  bman.o qman.o
+
+obj-$(CONFIG_FSL_BMAN_TEST)                     += bman-test.o
+bman-test-y                                      = bman_test.o
+bman-test-$(CONFIG_FSL_BMAN_TEST_API)           += bman_test_api.o
+
+obj-$(CONFIG_FSL_QMAN_TEST)                    += qman-test.o
+qman-test-y                                     = qman_test.o
+qman-test-$(CONFIG_FSL_QMAN_TEST_API)          += qman_test_api.o
+qman-test-$(CONFIG_FSL_QMAN_TEST_STASH)                += qman_test_stash.o
diff --git a/drivers/soc/fsl/qbman/bman.c b/drivers/soc/fsl/qbman/bman.c
new file mode 100644 (file)
index 0000000..ffa48fd
--- /dev/null
@@ -0,0 +1,797 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_priv.h"
+
+#define IRQNAME                "BMan portal %d"
+#define MAX_IRQNAME    16      /* big enough for "BMan portal %d" */
+
+/* Portal register assists */
+
+/* Cache-inhibited register offsets */
+#define BM_REG_RCR_PI_CINH     0x0000
+#define BM_REG_RCR_CI_CINH     0x0004
+#define BM_REG_RCR_ITR         0x0008
+#define BM_REG_CFG             0x0100
+#define BM_REG_SCN(n)          (0x0200 + ((n) << 2))
+#define BM_REG_ISR             0x0e00
+#define BM_REG_IER             0x0e04
+#define BM_REG_ISDR            0x0e08
+#define BM_REG_IIR             0x0e0c
+
+/* Cache-enabled register offsets */
+#define BM_CL_CR               0x0000
+#define BM_CL_RR0              0x0100
+#define BM_CL_RR1              0x0140
+#define BM_CL_RCR              0x1000
+#define BM_CL_RCR_PI_CENA      0x3000
+#define BM_CL_RCR_CI_CENA      0x3100
+
+/*
+ * Portal modes.
+ *   Enum types;
+ *     pmode == production mode
+ *     cmode == consumption mode,
+ *   Enum values use 3 letter codes. First letter matches the portal mode,
+ *   remaining two letters indicate;
+ *     ci == cache-inhibited portal register
+ *     ce == cache-enabled portal register
+ *     vb == in-band valid-bit (cache-enabled)
+ */
+enum bm_rcr_pmode {            /* matches BCSP_CFG::RPM */
+       bm_rcr_pci = 0,         /* PI index, cache-inhibited */
+       bm_rcr_pce = 1,         /* PI index, cache-enabled */
+       bm_rcr_pvb = 2          /* valid-bit */
+};
+enum bm_rcr_cmode {            /* s/w-only */
+       bm_rcr_cci,             /* CI index, cache-inhibited */
+       bm_rcr_cce              /* CI index, cache-enabled */
+};
+
+
+/* --- Portal structures --- */
+
+#define BM_RCR_SIZE            8
+
+/* Release Command */
+struct bm_rcr_entry {
+       union {
+               struct {
+                       u8 _ncw_verb; /* writes to this are non-coherent */
+                       u8 bpid; /* used with BM_RCR_VERB_CMD_BPID_SINGLE */
+                       u8 __reserved1[62];
+               };
+               struct bm_buffer bufs[8];
+       };
+};
+#define BM_RCR_VERB_VBIT               0x80
+#define BM_RCR_VERB_CMD_MASK           0x70    /* one of two values; */
+#define BM_RCR_VERB_CMD_BPID_SINGLE    0x20
+#define BM_RCR_VERB_CMD_BPID_MULTI     0x30
+#define BM_RCR_VERB_BUFCOUNT_MASK      0x0f    /* values 1..8 */
+
+struct bm_rcr {
+       struct bm_rcr_entry *ring, *cursor;
+       u8 ci, available, ithresh, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       u32 busy;
+       enum bm_rcr_pmode pmode;
+       enum bm_rcr_cmode cmode;
+#endif
+};
+
+/* MC (Management Command) command */
+struct bm_mc_command {
+       u8 _ncw_verb; /* writes to this are non-coherent */
+       u8 bpid; /* used by acquire command */
+       u8 __reserved[62];
+};
+#define BM_MCC_VERB_VBIT               0x80
+#define BM_MCC_VERB_CMD_MASK           0x70    /* where the verb contains; */
+#define BM_MCC_VERB_CMD_ACQUIRE                0x10
+#define BM_MCC_VERB_CMD_QUERY          0x40
+#define BM_MCC_VERB_ACQUIRE_BUFCOUNT   0x0f    /* values 1..8 go here */
+
+/* MC result, Acquire and Query Response */
+union bm_mc_result {
+       struct {
+               u8 verb;
+               u8 bpid;
+               u8 __reserved[62];
+       };
+       struct bm_buffer bufs[8];
+};
+#define BM_MCR_VERB_VBIT               0x80
+#define BM_MCR_VERB_CMD_MASK           BM_MCC_VERB_CMD_MASK
+#define BM_MCR_VERB_CMD_ACQUIRE                BM_MCC_VERB_CMD_ACQUIRE
+#define BM_MCR_VERB_CMD_QUERY          BM_MCC_VERB_CMD_QUERY
+#define BM_MCR_VERB_CMD_ERR_INVALID    0x60
+#define BM_MCR_VERB_CMD_ERR_ECC                0x70
+#define BM_MCR_VERB_ACQUIRE_BUFCOUNT   BM_MCC_VERB_ACQUIRE_BUFCOUNT /* 0..8 */
+#define BM_MCR_TIMEOUT                 10000 /* us */
+
+struct bm_mc {
+       struct bm_mc_command *cr;
+       union bm_mc_result *rr;
+       u8 rridx, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       enum {
+               /* Can only be _mc_start()ed */
+               mc_idle,
+               /* Can only be _mc_commit()ed or _mc_abort()ed */
+               mc_user,
+               /* Can only be _mc_retry()ed */
+               mc_hw
+       } state;
+#endif
+};
+
+struct bm_addr {
+       void __iomem *ce;       /* cache-enabled */
+       void __iomem *ci;       /* cache-inhibited */
+};
+
+struct bm_portal {
+       struct bm_addr addr;
+       struct bm_rcr rcr;
+       struct bm_mc mc;
+} ____cacheline_aligned;
+
+/* Cache-inhibited register access. */
+static inline u32 bm_in(struct bm_portal *p, u32 offset)
+{
+       return __raw_readl(p->addr.ci + offset);
+}
+
+static inline void bm_out(struct bm_portal *p, u32 offset, u32 val)
+{
+       __raw_writel(val, p->addr.ci + offset);
+}
+
+/* Cache Enabled Portal Access */
+static inline void bm_cl_invalidate(struct bm_portal *p, u32 offset)
+{
+       dpaa_invalidate(p->addr.ce + offset);
+}
+
+static inline void bm_cl_touch_ro(struct bm_portal *p, u32 offset)
+{
+       dpaa_touch_ro(p->addr.ce + offset);
+}
+
+static inline u32 bm_ce_in(struct bm_portal *p, u32 offset)
+{
+       return __raw_readl(p->addr.ce + offset);
+}
+
+struct bman_portal {
+       struct bm_portal p;
+       /* interrupt sources processed by portal_isr(), configurable */
+       unsigned long irq_sources;
+       /* probing time config params for cpu-affine portals */
+       const struct bm_portal_config *config;
+       char irqname[MAX_IRQNAME];
+};
+
+static cpumask_t affine_mask;
+static DEFINE_SPINLOCK(affine_mask_lock);
+static DEFINE_PER_CPU(struct bman_portal, bman_affine_portal);
+
+static inline struct bman_portal *get_affine_portal(void)
+{
+       return &get_cpu_var(bman_affine_portal);
+}
+
+static inline void put_affine_portal(void)
+{
+       put_cpu_var(bman_affine_portal);
+}
+
+/*
+ * This object type refers to a pool, it isn't *the* pool. There may be
+ * more than one such object per BMan buffer pool, eg. if different users of the
+ * pool are operating via different portals.
+ */
+struct bman_pool {
+       /* index of the buffer pool to encapsulate (0-63) */
+       u32 bpid;
+       /* Used for hash-table admin when using depletion notifications. */
+       struct bman_portal *portal;
+       struct bman_pool *next;
+};
+
+static u32 poll_portal_slow(struct bman_portal *p, u32 is);
+
+static irqreturn_t portal_isr(int irq, void *ptr)
+{
+       struct bman_portal *p = ptr;
+       struct bm_portal *portal = &p->p;
+       u32 clear = p->irq_sources;
+       u32 is = bm_in(portal, BM_REG_ISR) & p->irq_sources;
+
+       if (unlikely(!is))
+               return IRQ_NONE;
+
+       clear |= poll_portal_slow(p, is);
+       bm_out(portal, BM_REG_ISR, clear);
+       return IRQ_HANDLED;
+}
+
+/* --- RCR API --- */
+
+#define RCR_SHIFT      ilog2(sizeof(struct bm_rcr_entry))
+#define RCR_CARRY      (uintptr_t)(BM_RCR_SIZE << RCR_SHIFT)
+
+/* Bit-wise logic to wrap a ring pointer by clearing the "carry bit" */
+static struct bm_rcr_entry *rcr_carryclear(struct bm_rcr_entry *p)
+{
+       uintptr_t addr = (uintptr_t)p;
+
+       addr &= ~RCR_CARRY;
+
+       return (struct bm_rcr_entry *)addr;
+}
+
+#ifdef CONFIG_FSL_DPAA_CHECKING
+/* Bit-wise logic to convert a ring pointer to a ring index */
+static int rcr_ptr2idx(struct bm_rcr_entry *e)
+{
+       return ((uintptr_t)e >> RCR_SHIFT) & (BM_RCR_SIZE - 1);
+}
+#endif
+
+/* Increment the 'cursor' ring pointer, taking 'vbit' into account */
+static inline void rcr_inc(struct bm_rcr *rcr)
+{
+       /* increment to the next RCR pointer and handle overflow and 'vbit' */
+       struct bm_rcr_entry *partial = rcr->cursor + 1;
+
+       rcr->cursor = rcr_carryclear(partial);
+       if (partial != rcr->cursor)
+               rcr->vbit ^= BM_RCR_VERB_VBIT;
+}
+
+static int bm_rcr_get_avail(struct bm_portal *portal)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+
+       return rcr->available;
+}
+
+static int bm_rcr_get_fill(struct bm_portal *portal)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+
+       return BM_RCR_SIZE - 1 - rcr->available;
+}
+
+static void bm_rcr_set_ithresh(struct bm_portal *portal, u8 ithresh)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+
+       rcr->ithresh = ithresh;
+       bm_out(portal, BM_REG_RCR_ITR, ithresh);
+}
+
+static void bm_rcr_cce_prefetch(struct bm_portal *portal)
+{
+       __maybe_unused struct bm_rcr *rcr = &portal->rcr;
+
+       DPAA_ASSERT(rcr->cmode == bm_rcr_cce);
+       bm_cl_touch_ro(portal, BM_CL_RCR_CI_CENA);
+}
+
+static u8 bm_rcr_cce_update(struct bm_portal *portal)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+       u8 diff, old_ci = rcr->ci;
+
+       DPAA_ASSERT(rcr->cmode == bm_rcr_cce);
+       rcr->ci = bm_ce_in(portal, BM_CL_RCR_CI_CENA) & (BM_RCR_SIZE - 1);
+       bm_cl_invalidate(portal, BM_CL_RCR_CI_CENA);
+       diff = dpaa_cyc_diff(BM_RCR_SIZE, old_ci, rcr->ci);
+       rcr->available += diff;
+       return diff;
+}
+
+static inline struct bm_rcr_entry *bm_rcr_start(struct bm_portal *portal)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+
+       DPAA_ASSERT(!rcr->busy);
+       if (!rcr->available)
+               return NULL;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       rcr->busy = 1;
+#endif
+       dpaa_zero(rcr->cursor);
+       return rcr->cursor;
+}
+
+static inline void bm_rcr_pvb_commit(struct bm_portal *portal, u8 myverb)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+       struct bm_rcr_entry *rcursor;
+
+       DPAA_ASSERT(rcr->busy);
+       DPAA_ASSERT(rcr->pmode == bm_rcr_pvb);
+       DPAA_ASSERT(rcr->available >= 1);
+       dma_wmb();
+       rcursor = rcr->cursor;
+       rcursor->_ncw_verb = myverb | rcr->vbit;
+       dpaa_flush(rcursor);
+       rcr_inc(rcr);
+       rcr->available--;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       rcr->busy = 0;
+#endif
+}
+
+static int bm_rcr_init(struct bm_portal *portal, enum bm_rcr_pmode pmode,
+                      enum bm_rcr_cmode cmode)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+       u32 cfg;
+       u8 pi;
+
+       rcr->ring = portal->addr.ce + BM_CL_RCR;
+       rcr->ci = bm_in(portal, BM_REG_RCR_CI_CINH) & (BM_RCR_SIZE - 1);
+       pi = bm_in(portal, BM_REG_RCR_PI_CINH) & (BM_RCR_SIZE - 1);
+       rcr->cursor = rcr->ring + pi;
+       rcr->vbit = (bm_in(portal, BM_REG_RCR_PI_CINH) & BM_RCR_SIZE) ?
+               BM_RCR_VERB_VBIT : 0;
+       rcr->available = BM_RCR_SIZE - 1
+               - dpaa_cyc_diff(BM_RCR_SIZE, rcr->ci, pi);
+       rcr->ithresh = bm_in(portal, BM_REG_RCR_ITR);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       rcr->busy = 0;
+       rcr->pmode = pmode;
+       rcr->cmode = cmode;
+#endif
+       cfg = (bm_in(portal, BM_REG_CFG) & 0xffffffe0)
+               | (pmode & 0x3); /* BCSP_CFG::RPM */
+       bm_out(portal, BM_REG_CFG, cfg);
+       return 0;
+}
+
+static void bm_rcr_finish(struct bm_portal *portal)
+{
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       struct bm_rcr *rcr = &portal->rcr;
+       int i;
+
+       DPAA_ASSERT(!rcr->busy);
+
+       i = bm_in(portal, BM_REG_RCR_PI_CINH) & (BM_RCR_SIZE - 1);
+       if (i != rcr_ptr2idx(rcr->cursor))
+               pr_crit("losing uncommited RCR entries\n");
+
+       i = bm_in(portal, BM_REG_RCR_CI_CINH) & (BM_RCR_SIZE - 1);
+       if (i != rcr->ci)
+               pr_crit("missing existing RCR completions\n");
+       if (rcr->ci != rcr_ptr2idx(rcr->cursor))
+               pr_crit("RCR destroyed unquiesced\n");
+#endif
+}
+
+/* --- Management command API --- */
+static int bm_mc_init(struct bm_portal *portal)
+{
+       struct bm_mc *mc = &portal->mc;
+
+       mc->cr = portal->addr.ce + BM_CL_CR;
+       mc->rr = portal->addr.ce + BM_CL_RR0;
+       mc->rridx = (__raw_readb(&mc->cr->_ncw_verb) & BM_MCC_VERB_VBIT) ?
+                   0 : 1;
+       mc->vbit = mc->rridx ? BM_MCC_VERB_VBIT : 0;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = mc_idle;
+#endif
+       return 0;
+}
+
+static void bm_mc_finish(struct bm_portal *portal)
+{
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       struct bm_mc *mc = &portal->mc;
+
+       DPAA_ASSERT(mc->state == mc_idle);
+       if (mc->state != mc_idle)
+               pr_crit("Losing incomplete MC command\n");
+#endif
+}
+
+static inline struct bm_mc_command *bm_mc_start(struct bm_portal *portal)
+{
+       struct bm_mc *mc = &portal->mc;
+
+       DPAA_ASSERT(mc->state == mc_idle);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = mc_user;
+#endif
+       dpaa_zero(mc->cr);
+       return mc->cr;
+}
+
+static inline void bm_mc_commit(struct bm_portal *portal, u8 myverb)
+{
+       struct bm_mc *mc = &portal->mc;
+       union bm_mc_result *rr = mc->rr + mc->rridx;
+
+       DPAA_ASSERT(mc->state == mc_user);
+       dma_wmb();
+       mc->cr->_ncw_verb = myverb | mc->vbit;
+       dpaa_flush(mc->cr);
+       dpaa_invalidate_touch_ro(rr);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = mc_hw;
+#endif
+}
+
+static inline union bm_mc_result *bm_mc_result(struct bm_portal *portal)
+{
+       struct bm_mc *mc = &portal->mc;
+       union bm_mc_result *rr = mc->rr + mc->rridx;
+
+       DPAA_ASSERT(mc->state == mc_hw);
+       /*
+        * The inactive response register's verb byte always returns zero until
+        * its command is submitted and completed. This includes the valid-bit,
+        * in case you were wondering...
+        */
+       if (!__raw_readb(&rr->verb)) {
+               dpaa_invalidate_touch_ro(rr);
+               return NULL;
+       }
+       mc->rridx ^= 1;
+       mc->vbit ^= BM_MCC_VERB_VBIT;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = mc_idle;
+#endif
+       return rr;
+}
+
+static inline int bm_mc_result_timeout(struct bm_portal *portal,
+                                      union bm_mc_result **mcr)
+{
+       int timeout = BM_MCR_TIMEOUT;
+
+       do {
+               *mcr = bm_mc_result(portal);
+               if (*mcr)
+                       break;
+               udelay(1);
+       } while (--timeout);
+
+       return timeout;
+}
+
+/* Disable all BSCN interrupts for the portal */
+static void bm_isr_bscn_disable(struct bm_portal *portal)
+{
+       bm_out(portal, BM_REG_SCN(0), 0);
+       bm_out(portal, BM_REG_SCN(1), 0);
+}
+
+static int bman_create_portal(struct bman_portal *portal,
+                             const struct bm_portal_config *c)
+{
+       struct bm_portal *p;
+       int ret;
+
+       p = &portal->p;
+       /*
+        * prep the low-level portal struct with the mapped addresses from the
+        * config, everything that follows depends on it and "config" is more
+        * for (de)reference...
+        */
+       p->addr.ce = c->addr_virt[DPAA_PORTAL_CE];
+       p->addr.ci = c->addr_virt[DPAA_PORTAL_CI];
+       if (bm_rcr_init(p, bm_rcr_pvb, bm_rcr_cce)) {
+               dev_err(c->dev, "RCR initialisation failed\n");
+               goto fail_rcr;
+       }
+       if (bm_mc_init(p)) {
+               dev_err(c->dev, "MC initialisation failed\n");
+               goto fail_mc;
+       }
+       /*
+        * Default to all BPIDs disabled, we enable as required at
+        * run-time.
+        */
+       bm_isr_bscn_disable(p);
+
+       /* Write-to-clear any stale interrupt status bits */
+       bm_out(p, BM_REG_ISDR, 0xffffffff);
+       portal->irq_sources = 0;
+       bm_out(p, BM_REG_IER, 0);
+       bm_out(p, BM_REG_ISR, 0xffffffff);
+       snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, c->cpu);
+       if (request_irq(c->irq, portal_isr, 0, portal->irqname, portal)) {
+               dev_err(c->dev, "request_irq() failed\n");
+               goto fail_irq;
+       }
+       if (c->cpu != -1 && irq_can_set_affinity(c->irq) &&
+           irq_set_affinity(c->irq, cpumask_of(c->cpu))) {
+               dev_err(c->dev, "irq_set_affinity() failed\n");
+               goto fail_affinity;
+       }
+
+       /* Need RCR to be empty before continuing */
+       ret = bm_rcr_get_fill(p);
+       if (ret) {
+               dev_err(c->dev, "RCR unclean\n");
+               goto fail_rcr_empty;
+       }
+       /* Success */
+       portal->config = c;
+
+       bm_out(p, BM_REG_ISDR, 0);
+       bm_out(p, BM_REG_IIR, 0);
+
+       return 0;
+
+fail_rcr_empty:
+fail_affinity:
+       free_irq(c->irq, portal);
+fail_irq:
+       bm_mc_finish(p);
+fail_mc:
+       bm_rcr_finish(p);
+fail_rcr:
+       return -EIO;
+}
+
+struct bman_portal *bman_create_affine_portal(const struct bm_portal_config *c)
+{
+       struct bman_portal *portal;
+       int err;
+
+       portal = &per_cpu(bman_affine_portal, c->cpu);
+       err = bman_create_portal(portal, c);
+       if (err)
+               return NULL;
+
+       spin_lock(&affine_mask_lock);
+       cpumask_set_cpu(c->cpu, &affine_mask);
+       spin_unlock(&affine_mask_lock);
+
+       return portal;
+}
+
+static u32 poll_portal_slow(struct bman_portal *p, u32 is)
+{
+       u32 ret = is;
+
+       if (is & BM_PIRQ_RCRI) {
+               bm_rcr_cce_update(&p->p);
+               bm_rcr_set_ithresh(&p->p, 0);
+               bm_out(&p->p, BM_REG_ISR, BM_PIRQ_RCRI);
+               is &= ~BM_PIRQ_RCRI;
+       }
+
+       /* There should be no status register bits left undefined */
+       DPAA_ASSERT(!is);
+       return ret;
+}
+
+int bman_p_irqsource_add(struct bman_portal *p, u32 bits)
+{
+       unsigned long irqflags;
+
+       local_irq_save(irqflags);
+       set_bits(bits & BM_PIRQ_VISIBLE, &p->irq_sources);
+       bm_out(&p->p, BM_REG_IER, p->irq_sources);
+       local_irq_restore(irqflags);
+       return 0;
+}
+
+static int bm_shutdown_pool(u32 bpid)
+{
+       struct bm_mc_command *bm_cmd;
+       union bm_mc_result *bm_res;
+
+       while (1) {
+               struct bman_portal *p = get_affine_portal();
+               /* Acquire buffers until empty */
+               bm_cmd = bm_mc_start(&p->p);
+               bm_cmd->bpid = bpid;
+               bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE | 1);
+               if (!bm_mc_result_timeout(&p->p, &bm_res)) {
+                       put_affine_portal();
+                       pr_crit("BMan Acquire Command timedout\n");
+                       return -ETIMEDOUT;
+               }
+               if (!(bm_res->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT)) {
+                       put_affine_portal();
+                       /* Pool is empty */
+                       return 0;
+               }
+               put_affine_portal();
+       }
+
+       return 0;
+}
+
+struct gen_pool *bm_bpalloc;
+
+static int bm_alloc_bpid_range(u32 *result, u32 count)
+{
+       unsigned long addr;
+
+       addr = gen_pool_alloc(bm_bpalloc, count);
+       if (!addr)
+               return -ENOMEM;
+
+       *result = addr & ~DPAA_GENALLOC_OFF;
+
+       return 0;
+}
+
+static int bm_release_bpid(u32 bpid)
+{
+       int ret;
+
+       ret = bm_shutdown_pool(bpid);
+       if (ret) {
+               pr_debug("BPID %d leaked\n", bpid);
+               return ret;
+       }
+
+       gen_pool_free(bm_bpalloc, bpid | DPAA_GENALLOC_OFF, 1);
+       return 0;
+}
+
+struct bman_pool *bman_new_pool(void)
+{
+       struct bman_pool *pool = NULL;
+       u32 bpid;
+
+       if (bm_alloc_bpid_range(&bpid, 1))
+               return NULL;
+
+       pool = kmalloc(sizeof(*pool), GFP_KERNEL);
+       if (!pool)
+               goto err;
+
+       pool->bpid = bpid;
+
+       return pool;
+err:
+       bm_release_bpid(bpid);
+       kfree(pool);
+       return NULL;
+}
+EXPORT_SYMBOL(bman_new_pool);
+
+void bman_free_pool(struct bman_pool *pool)
+{
+       bm_release_bpid(pool->bpid);
+
+       kfree(pool);
+}
+EXPORT_SYMBOL(bman_free_pool);
+
+int bman_get_bpid(const struct bman_pool *pool)
+{
+       return pool->bpid;
+}
+EXPORT_SYMBOL(bman_get_bpid);
+
+static void update_rcr_ci(struct bman_portal *p, int avail)
+{
+       if (avail)
+               bm_rcr_cce_prefetch(&p->p);
+       else
+               bm_rcr_cce_update(&p->p);
+}
+
+int bman_release(struct bman_pool *pool, const struct bm_buffer *bufs, u8 num)
+{
+       struct bman_portal *p;
+       struct bm_rcr_entry *r;
+       unsigned long irqflags;
+       int avail, timeout = 1000; /* 1ms */
+       int i = num - 1;
+
+       DPAA_ASSERT(num > 0 && num <= 8);
+
+       do {
+               p = get_affine_portal();
+               local_irq_save(irqflags);
+               avail = bm_rcr_get_avail(&p->p);
+               if (avail < 2)
+                       update_rcr_ci(p, avail);
+               r = bm_rcr_start(&p->p);
+               local_irq_restore(irqflags);
+               put_affine_portal();
+               if (likely(r))
+                       break;
+
+               udelay(1);
+       } while (--timeout);
+
+       if (unlikely(!timeout))
+               return -ETIMEDOUT;
+
+       p = get_affine_portal();
+       local_irq_save(irqflags);
+       /*
+        * we can copy all but the first entry, as this can trigger badness
+        * with the valid-bit
+        */
+       bm_buffer_set64(r->bufs, bm_buffer_get64(bufs));
+       bm_buffer_set_bpid(r->bufs, pool->bpid);
+       if (i)
+               memcpy(&r->bufs[1], &bufs[1], i * sizeof(bufs[0]));
+
+       bm_rcr_pvb_commit(&p->p, BM_RCR_VERB_CMD_BPID_SINGLE |
+                         (num & BM_RCR_VERB_BUFCOUNT_MASK));
+
+       local_irq_restore(irqflags);
+       put_affine_portal();
+       return 0;
+}
+EXPORT_SYMBOL(bman_release);
+
+int bman_acquire(struct bman_pool *pool, struct bm_buffer *bufs, u8 num)
+{
+       struct bman_portal *p = get_affine_portal();
+       struct bm_mc_command *mcc;
+       union bm_mc_result *mcr;
+       int ret;
+
+       DPAA_ASSERT(num > 0 && num <= 8);
+
+       mcc = bm_mc_start(&p->p);
+       mcc->bpid = pool->bpid;
+       bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE |
+                    (num & BM_MCC_VERB_ACQUIRE_BUFCOUNT));
+       if (!bm_mc_result_timeout(&p->p, &mcr)) {
+               put_affine_portal();
+               pr_crit("BMan Acquire Timeout\n");
+               return -ETIMEDOUT;
+       }
+       ret = mcr->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT;
+       if (bufs)
+               memcpy(&bufs[0], &mcr->bufs[0], num * sizeof(bufs[0]));
+
+       put_affine_portal();
+       if (ret != num)
+               ret = -ENOMEM;
+       return ret;
+}
+EXPORT_SYMBOL(bman_acquire);
+
+const struct bm_portal_config *
+bman_get_bm_portal_config(const struct bman_portal *portal)
+{
+       return portal->config;
+}
diff --git a/drivers/soc/fsl/qbman/bman_ccsr.c b/drivers/soc/fsl/qbman/bman_ccsr.c
new file mode 100644 (file)
index 0000000..9deb052
--- /dev/null
@@ -0,0 +1,263 @@
+/* Copyright (c) 2009 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_priv.h"
+
+u16 bman_ip_rev;
+EXPORT_SYMBOL(bman_ip_rev);
+
+/* Register offsets */
+#define REG_FBPR_FPC           0x0800
+#define REG_ECSR               0x0a00
+#define REG_ECIR               0x0a04
+#define REG_EADR               0x0a08
+#define REG_EDATA(n)           (0x0a10 + ((n) * 0x04))
+#define REG_SBEC(n)            (0x0a80 + ((n) * 0x04))
+#define REG_IP_REV_1           0x0bf8
+#define REG_IP_REV_2           0x0bfc
+#define REG_FBPR_BARE          0x0c00
+#define REG_FBPR_BAR           0x0c04
+#define REG_FBPR_AR            0x0c10
+#define REG_SRCIDR             0x0d04
+#define REG_LIODNR             0x0d08
+#define REG_ERR_ISR            0x0e00
+#define REG_ERR_IER            0x0e04
+#define REG_ERR_ISDR           0x0e08
+
+/* Used by all error interrupt registers except 'inhibit' */
+#define BM_EIRQ_IVCI   0x00000010      /* Invalid Command Verb */
+#define BM_EIRQ_FLWI   0x00000008      /* FBPR Low Watermark */
+#define BM_EIRQ_MBEI   0x00000004      /* Multi-bit ECC Error */
+#define BM_EIRQ_SBEI   0x00000002      /* Single-bit ECC Error */
+#define BM_EIRQ_BSCN   0x00000001      /* pool State Change Notification */
+
+struct bman_hwerr_txt {
+       u32 mask;
+       const char *txt;
+};
+
+static const struct bman_hwerr_txt bman_hwerr_txts[] = {
+       { BM_EIRQ_IVCI, "Invalid Command Verb" },
+       { BM_EIRQ_FLWI, "FBPR Low Watermark" },
+       { BM_EIRQ_MBEI, "Multi-bit ECC Error" },
+       { BM_EIRQ_SBEI, "Single-bit ECC Error" },
+       { BM_EIRQ_BSCN, "Pool State Change Notification" },
+};
+
+/* Only trigger low water mark interrupt once only */
+#define BMAN_ERRS_TO_DISABLE BM_EIRQ_FLWI
+
+/* Pointer to the start of the BMan's CCSR space */
+static u32 __iomem *bm_ccsr_start;
+
+static inline u32 bm_ccsr_in(u32 offset)
+{
+       return ioread32be(bm_ccsr_start + offset/4);
+}
+static inline void bm_ccsr_out(u32 offset, u32 val)
+{
+       iowrite32be(val, bm_ccsr_start + offset/4);
+}
+
+static void bm_get_version(u16 *id, u8 *major, u8 *minor)
+{
+       u32 v = bm_ccsr_in(REG_IP_REV_1);
+       *id = (v >> 16);
+       *major = (v >> 8) & 0xff;
+       *minor = v & 0xff;
+}
+
+/* signal transactions for FBPRs with higher priority */
+#define FBPR_AR_RPRIO_HI BIT(30)
+
+static void bm_set_memory(u64 ba, u32 size)
+{
+       u32 exp = ilog2(size);
+       /* choke if size isn't within range */
+       DPAA_ASSERT(size >= 4096 && size <= 1024*1024*1024 &&
+                  is_power_of_2(size));
+       /* choke if '[e]ba' has lower-alignment than 'size' */
+       DPAA_ASSERT(!(ba & (size - 1)));
+       bm_ccsr_out(REG_FBPR_BARE, upper_32_bits(ba));
+       bm_ccsr_out(REG_FBPR_BAR, lower_32_bits(ba));
+       bm_ccsr_out(REG_FBPR_AR, exp - 1);
+}
+
+/*
+ * Location and size of BMan private memory
+ *
+ * Ideally we would use the DMA API to turn rmem->base into a DMA address
+ * (especially if iommu translations ever get involved).  Unfortunately, the
+ * DMA API currently does not allow mapping anything that is not backed with
+ * a struct page.
+ */
+static dma_addr_t fbpr_a;
+static size_t fbpr_sz;
+
+static int bman_fbpr(struct reserved_mem *rmem)
+{
+       fbpr_a = rmem->base;
+       fbpr_sz = rmem->size;
+
+       WARN_ON(!(fbpr_a && fbpr_sz));
+
+       return 0;
+}
+RESERVEDMEM_OF_DECLARE(bman_fbpr, "fsl,bman-fbpr", bman_fbpr);
+
+static irqreturn_t bman_isr(int irq, void *ptr)
+{
+       u32 isr_val, ier_val, ecsr_val, isr_mask, i;
+       struct device *dev = ptr;
+
+       ier_val = bm_ccsr_in(REG_ERR_IER);
+       isr_val = bm_ccsr_in(REG_ERR_ISR);
+       ecsr_val = bm_ccsr_in(REG_ECSR);
+       isr_mask = isr_val & ier_val;
+
+       if (!isr_mask)
+               return IRQ_NONE;
+
+       for (i = 0; i < ARRAY_SIZE(bman_hwerr_txts); i++) {
+               if (bman_hwerr_txts[i].mask & isr_mask) {
+                       dev_err_ratelimited(dev, "ErrInt: %s\n",
+                                           bman_hwerr_txts[i].txt);
+                       if (bman_hwerr_txts[i].mask & ecsr_val) {
+                               /* Re-arm error capture registers */
+                               bm_ccsr_out(REG_ECSR, ecsr_val);
+                       }
+                       if (bman_hwerr_txts[i].mask & BMAN_ERRS_TO_DISABLE) {
+                               dev_dbg(dev, "Disabling error 0x%x\n",
+                                       bman_hwerr_txts[i].mask);
+                               ier_val &= ~bman_hwerr_txts[i].mask;
+                               bm_ccsr_out(REG_ERR_IER, ier_val);
+                       }
+               }
+       }
+       bm_ccsr_out(REG_ERR_ISR, isr_val);
+
+       return IRQ_HANDLED;
+}
+
+static int fsl_bman_probe(struct platform_device *pdev)
+{
+       int ret, err_irq;
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct resource *res;
+       u16 id, bm_pool_cnt;
+       u8 major, minor;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "Can't get %s property 'IORESOURCE_MEM'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+       bm_ccsr_start = devm_ioremap(dev, res->start,
+                                    res->end - res->start + 1);
+       if (!bm_ccsr_start)
+               return -ENXIO;
+
+       bm_get_version(&id, &major, &minor);
+       if (major == 1 && minor == 0) {
+               bman_ip_rev = BMAN_REV10;
+               bm_pool_cnt = BM_POOL_MAX;
+       } else if (major == 2 && minor == 0) {
+               bman_ip_rev = BMAN_REV20;
+               bm_pool_cnt = 8;
+       } else if (major == 2 && minor == 1) {
+               bman_ip_rev = BMAN_REV21;
+               bm_pool_cnt = BM_POOL_MAX;
+       } else {
+               dev_err(dev, "Unknown Bman version:%04x,%02x,%02x\n",
+                       id, major, minor);
+               return -ENODEV;
+       }
+
+       bm_set_memory(fbpr_a, fbpr_sz);
+
+       err_irq = platform_get_irq(pdev, 0);
+       if (err_irq <= 0) {
+               dev_info(dev, "Can't get %s IRQ\n", node->full_name);
+               return -ENODEV;
+       }
+       ret = devm_request_irq(dev, err_irq, bman_isr, IRQF_SHARED, "bman-err",
+                              dev);
+       if (ret)  {
+               dev_err(dev, "devm_request_irq() failed %d for '%s'\n",
+                       ret, node->full_name);
+               return ret;
+       }
+       /* Disable Buffer Pool State Change */
+       bm_ccsr_out(REG_ERR_ISDR, BM_EIRQ_BSCN);
+       /*
+        * Write-to-clear any stale bits, (eg. starvation being asserted prior
+        * to resource allocation during driver init).
+        */
+       bm_ccsr_out(REG_ERR_ISR, 0xffffffff);
+       /* Enable Error Interrupts */
+       bm_ccsr_out(REG_ERR_IER, 0xffffffff);
+
+       bm_bpalloc = devm_gen_pool_create(dev, 0, -1, "bman-bpalloc");
+       if (IS_ERR(bm_bpalloc)) {
+               ret = PTR_ERR(bm_bpalloc);
+               dev_err(dev, "bman-bpalloc pool init failed (%d)\n", ret);
+               return ret;
+       }
+
+       /* seed BMan resource pool */
+       ret = gen_pool_add(bm_bpalloc, DPAA_GENALLOC_OFF, bm_pool_cnt, -1);
+       if (ret) {
+               dev_err(dev, "Failed to seed BPID range [%d..%d] (%d)\n",
+                       0, bm_pool_cnt - 1, ret);
+               return ret;
+       }
+
+       return 0;
+};
+
+static const struct of_device_id fsl_bman_ids[] = {
+       {
+               .compatible = "fsl,bman",
+       },
+       {}
+};
+
+static struct platform_driver fsl_bman_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = fsl_bman_ids,
+               .suppress_bind_attrs = true,
+       },
+       .probe = fsl_bman_probe,
+};
+
+builtin_platform_driver(fsl_bman_driver);
diff --git a/drivers/soc/fsl/qbman/bman_portal.c b/drivers/soc/fsl/qbman/bman_portal.c
new file mode 100644 (file)
index 0000000..6579cc1
--- /dev/null
@@ -0,0 +1,219 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_priv.h"
+
+static struct bman_portal *affine_bportals[NR_CPUS];
+static struct cpumask portal_cpus;
+/* protect bman global registers and global data shared among portals */
+static DEFINE_SPINLOCK(bman_lock);
+
+static struct bman_portal *init_pcfg(struct bm_portal_config *pcfg)
+{
+       struct bman_portal *p = bman_create_affine_portal(pcfg);
+
+       if (!p) {
+               dev_crit(pcfg->dev, "%s: Portal failure on cpu %d\n",
+                        __func__, pcfg->cpu);
+               return NULL;
+       }
+
+       bman_p_irqsource_add(p, BM_PIRQ_RCRI);
+       affine_bportals[pcfg->cpu] = p;
+
+       dev_info(pcfg->dev, "Portal initialised, cpu %d\n", pcfg->cpu);
+
+       return p;
+}
+
+static void bman_offline_cpu(unsigned int cpu)
+{
+       struct bman_portal *p = affine_bportals[cpu];
+       const struct bm_portal_config *pcfg;
+
+       if (!p)
+               return;
+
+       pcfg = bman_get_bm_portal_config(p);
+       if (!pcfg)
+               return;
+
+       irq_set_affinity(pcfg->irq, cpumask_of(0));
+}
+
+static void bman_online_cpu(unsigned int cpu)
+{
+       struct bman_portal *p = affine_bportals[cpu];
+       const struct bm_portal_config *pcfg;
+
+       if (!p)
+               return;
+
+       pcfg = bman_get_bm_portal_config(p);
+       if (!pcfg)
+               return;
+
+       irq_set_affinity(pcfg->irq, cpumask_of(cpu));
+}
+
+static int bman_hotplug_cpu_callback(struct notifier_block *nfb,
+                                    unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               bman_online_cpu(cpu);
+               break;
+       case CPU_DOWN_PREPARE:
+       case CPU_DOWN_PREPARE_FROZEN:
+               bman_offline_cpu(cpu);
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block bman_hotplug_cpu_notifier = {
+       .notifier_call = bman_hotplug_cpu_callback,
+};
+
+static int bman_portal_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct bm_portal_config *pcfg;
+       struct resource *addr_phys[2];
+       void __iomem *va;
+       int irq, cpu;
+
+       pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
+       if (!pcfg)
+               return -ENOMEM;
+
+       pcfg->dev = dev;
+
+       addr_phys[0] = platform_get_resource(pdev, IORESOURCE_MEM,
+                                            DPAA_PORTAL_CE);
+       if (!addr_phys[0]) {
+               dev_err(dev, "Can't get %s property 'reg::CE'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+
+       addr_phys[1] = platform_get_resource(pdev, IORESOURCE_MEM,
+                                            DPAA_PORTAL_CI);
+       if (!addr_phys[1]) {
+               dev_err(dev, "Can't get %s property 'reg::CI'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+
+       pcfg->cpu = -1;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq <= 0) {
+               dev_err(dev, "Can't get %s IRQ'\n", node->full_name);
+               return -ENXIO;
+       }
+       pcfg->irq = irq;
+
+       va = ioremap_prot(addr_phys[0]->start, resource_size(addr_phys[0]), 0);
+       if (!va)
+               goto err_ioremap1;
+
+       pcfg->addr_virt[DPAA_PORTAL_CE] = va;
+
+       va = ioremap_prot(addr_phys[1]->start, resource_size(addr_phys[1]),
+                         _PAGE_GUARDED | _PAGE_NO_CACHE);
+       if (!va)
+               goto err_ioremap2;
+
+       pcfg->addr_virt[DPAA_PORTAL_CI] = va;
+
+       spin_lock(&bman_lock);
+       cpu = cpumask_next_zero(-1, &portal_cpus);
+       if (cpu >= nr_cpu_ids) {
+               /* unassigned portal, skip init */
+               spin_unlock(&bman_lock);
+               return 0;
+       }
+
+       cpumask_set_cpu(cpu, &portal_cpus);
+       spin_unlock(&bman_lock);
+       pcfg->cpu = cpu;
+
+       if (!init_pcfg(pcfg))
+               goto err_ioremap2;
+
+       /* clear irq affinity if assigned cpu is offline */
+       if (!cpu_online(cpu))
+               bman_offline_cpu(cpu);
+
+       return 0;
+
+err_ioremap2:
+       iounmap(pcfg->addr_virt[DPAA_PORTAL_CE]);
+err_ioremap1:
+       dev_err(dev, "ioremap failed\n");
+       return -ENXIO;
+}
+
+static const struct of_device_id bman_portal_ids[] = {
+       {
+               .compatible = "fsl,bman-portal",
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, bman_portal_ids);
+
+static struct platform_driver bman_portal_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = bman_portal_ids,
+       },
+       .probe = bman_portal_probe,
+};
+
+static int __init bman_portal_driver_register(struct platform_driver *drv)
+{
+       int ret;
+
+       ret = platform_driver_register(drv);
+       if (ret < 0)
+               return ret;
+
+       register_hotcpu_notifier(&bman_hotplug_cpu_notifier);
+
+       return 0;
+}
+
+module_driver(bman_portal_driver,
+             bman_portal_driver_register, platform_driver_unregister);
diff --git a/drivers/soc/fsl/qbman/bman_priv.h b/drivers/soc/fsl/qbman/bman_priv.h
new file mode 100644 (file)
index 0000000..f6896a2
--- /dev/null
@@ -0,0 +1,80 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "dpaa_sys.h"
+
+#include <soc/fsl/bman.h>
+
+/* Portal processing (interrupt) sources */
+#define BM_PIRQ_RCRI   0x00000002      /* RCR Ring (below threshold) */
+
+/* Revision info (for errata and feature handling) */
+#define BMAN_REV10 0x0100
+#define BMAN_REV20 0x0200
+#define BMAN_REV21 0x0201
+extern u16 bman_ip_rev;        /* 0 if uninitialised, otherwise BMAN_REVx */
+
+extern struct gen_pool *bm_bpalloc;
+
+struct bm_portal_config {
+       /*
+        * Corenet portal addresses;
+        * [0]==cache-enabled, [1]==cache-inhibited.
+        */
+       void __iomem *addr_virt[2];
+       /* Allow these to be joined in lists */
+       struct list_head list;
+       struct device *dev;
+       /* User-visible portal configuration settings */
+       /* portal is affined to this cpu */
+       int cpu;
+       /* portal interrupt line */
+       int irq;
+};
+
+struct bman_portal *bman_create_affine_portal(
+                       const struct bm_portal_config *config);
+/*
+ * The below bman_p_***() variant might be called in a situation that the cpu
+ * which the portal affine to is not online yet.
+ * @bman_portal specifies which portal the API will use.
+ */
+int bman_p_irqsource_add(struct bman_portal *p, u32 bits);
+
+/*
+ * Used by all portal interrupt registers except 'inhibit'
+ * This mask contains all the "irqsource" bits visible to API users
+ */
+#define BM_PIRQ_VISIBLE        BM_PIRQ_RCRI
+
+const struct bm_portal_config *
+bman_get_bm_portal_config(const struct bman_portal *portal);
diff --git a/drivers/soc/fsl/qbman/bman_test.c b/drivers/soc/fsl/qbman/bman_test.c
new file mode 100644 (file)
index 0000000..09b1c96
--- /dev/null
@@ -0,0 +1,53 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_test.h"
+
+MODULE_AUTHOR("Geoff Thorpe");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("BMan testing");
+
+static int test_init(void)
+{
+#ifdef CONFIG_FSL_BMAN_TEST_API
+       int loop = 1;
+
+       while (loop--)
+               bman_test_api();
+#endif
+       return 0;
+}
+
+static void test_exit(void)
+{
+}
+
+module_init(test_init);
+module_exit(test_exit);
diff --git a/drivers/soc/fsl/qbman/bman_test.h b/drivers/soc/fsl/qbman/bman_test.h
new file mode 100644 (file)
index 0000000..037ed34
--- /dev/null
@@ -0,0 +1,35 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_priv.h"
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+void bman_test_api(void);
diff --git a/drivers/soc/fsl/qbman/bman_test_api.c b/drivers/soc/fsl/qbman/bman_test_api.c
new file mode 100644 (file)
index 0000000..6f6bdd1
--- /dev/null
@@ -0,0 +1,151 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_test.h"
+
+#define NUM_BUFS       93
+#define LOOPS          3
+#define BMAN_TOKEN_MASK 0x00FFFFFFFFFFLLU
+
+static struct bman_pool *pool;
+static struct bm_buffer bufs_in[NUM_BUFS] ____cacheline_aligned;
+static struct bm_buffer bufs_out[NUM_BUFS] ____cacheline_aligned;
+static int bufs_received;
+
+static void bufs_init(void)
+{
+       int i;
+
+       for (i = 0; i < NUM_BUFS; i++)
+               bm_buffer_set64(&bufs_in[i], 0xfedc01234567LLU * i);
+       bufs_received = 0;
+}
+
+static inline int bufs_cmp(const struct bm_buffer *a, const struct bm_buffer *b)
+{
+       if (bman_ip_rev == BMAN_REV20 || bman_ip_rev == BMAN_REV21) {
+
+               /*
+                * On SoCs with BMan revison 2.0, BMan only respects the 40
+                * LS-bits of buffer addresses, masking off the upper 8-bits on
+                * release commands. The API provides for 48-bit addresses
+                * because some SoCs support all 48-bits. When generating
+                * garbage addresses for testing, we either need to zero the
+                * upper 8-bits when releasing to BMan (otherwise we'll be
+                * disappointed when the buffers we acquire back from BMan
+                * don't match), or we need to mask the upper 8-bits off when
+                * comparing. We do the latter.
+                */
+               if ((bm_buffer_get64(a) & BMAN_TOKEN_MASK) <
+                   (bm_buffer_get64(b) & BMAN_TOKEN_MASK))
+                       return -1;
+               if ((bm_buffer_get64(a) & BMAN_TOKEN_MASK) >
+                   (bm_buffer_get64(b) & BMAN_TOKEN_MASK))
+                       return 1;
+       } else {
+               if (bm_buffer_get64(a) < bm_buffer_get64(b))
+                       return -1;
+               if (bm_buffer_get64(a) > bm_buffer_get64(b))
+                       return 1;
+       }
+
+       return 0;
+}
+
+static void bufs_confirm(void)
+{
+       int i, j;
+
+       for (i = 0; i < NUM_BUFS; i++) {
+               int matches = 0;
+
+               for (j = 0; j < NUM_BUFS; j++)
+                       if (!bufs_cmp(&bufs_in[i], &bufs_out[j]))
+                               matches++;
+               WARN_ON(matches != 1);
+       }
+}
+
+/* test */
+void bman_test_api(void)
+{
+       int i, loops = LOOPS;
+
+       bufs_init();
+
+       pr_info("%s(): Starting\n", __func__);
+
+       pool = bman_new_pool();
+       if (!pool) {
+               pr_crit("bman_new_pool() failed\n");
+               goto failed;
+       }
+
+       /* Release buffers */
+do_loop:
+       i = 0;
+       while (i < NUM_BUFS) {
+               int num = 8;
+
+               if (i + num > NUM_BUFS)
+                       num = NUM_BUFS - i;
+               if (bman_release(pool, bufs_in + i, num)) {
+                       pr_crit("bman_release() failed\n");
+                       goto failed;
+               }
+               i += num;
+       }
+
+       /* Acquire buffers */
+       while (i > 0) {
+               int tmp, num = 8;
+
+               if (num > i)
+                       num = i;
+               tmp = bman_acquire(pool, bufs_out + i - num, num);
+               WARN_ON(tmp != num);
+               i -= num;
+       }
+       i = bman_acquire(pool, NULL, 1);
+       WARN_ON(i > 0);
+
+       bufs_confirm();
+
+       if (--loops)
+               goto do_loop;
+
+       /* Clean up */
+       bman_free_pool(pool);
+       pr_info("%s(): Finished\n", __func__);
+       return;
+
+failed:
+       WARN_ON(1);
+}
diff --git a/drivers/soc/fsl/qbman/dpaa_sys.h b/drivers/soc/fsl/qbman/dpaa_sys.h
new file mode 100644 (file)
index 0000000..b63fd72
--- /dev/null
@@ -0,0 +1,103 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __DPAA_SYS_H
+#define __DPAA_SYS_H
+
+#include <linux/cpu.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+#include <linux/platform_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/prefetch.h>
+#include <linux/genalloc.h>
+#include <asm/cacheflush.h>
+
+/* For 2-element tables related to cache-inhibited and cache-enabled mappings */
+#define DPAA_PORTAL_CE 0
+#define DPAA_PORTAL_CI 1
+
+#if (L1_CACHE_BYTES != 32) && (L1_CACHE_BYTES != 64)
+#error "Unsupported Cacheline Size"
+#endif
+
+static inline void dpaa_flush(void *p)
+{
+#ifdef CONFIG_PPC
+       flush_dcache_range((unsigned long)p, (unsigned long)p+64);
+#elif defined(CONFIG_ARM32)
+       __cpuc_flush_dcache_area(p, 64);
+#elif defined(CONFIG_ARM64)
+       __flush_dcache_area(p, 64);
+#endif
+}
+
+#define dpaa_invalidate(p) dpaa_flush(p)
+
+#define dpaa_zero(p) memset(p, 0, 64)
+
+static inline void dpaa_touch_ro(void *p)
+{
+#if (L1_CACHE_BYTES == 32)
+       prefetch(p+32);
+#endif
+       prefetch(p);
+}
+
+/* Commonly used combo */
+static inline void dpaa_invalidate_touch_ro(void *p)
+{
+       dpaa_invalidate(p);
+       dpaa_touch_ro(p);
+}
+
+
+#ifdef CONFIG_FSL_DPAA_CHECKING
+#define DPAA_ASSERT(x) WARN_ON(!(x))
+#else
+#define DPAA_ASSERT(x)
+#endif
+
+/* cyclic helper for rings */
+static inline u8 dpaa_cyc_diff(u8 ringsize, u8 first, u8 last)
+{
+       /* 'first' is included, 'last' is excluded */
+       if (first <= last)
+               return last - first;
+       return ringsize + last - first;
+}
+
+/* Offset applied to genalloc pools due to zero being an error return */
+#define DPAA_GENALLOC_OFF      0x80000000
+
+#endif /* __DPAA_SYS_H */
diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c
new file mode 100644 (file)
index 0000000..119054b
--- /dev/null
@@ -0,0 +1,2881 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_priv.h"
+
+#define DQRR_MAXFILL   15
+#define EQCR_ITHRESH   4       /* if EQCR congests, interrupt threshold */
+#define IRQNAME                "QMan portal %d"
+#define MAX_IRQNAME    16      /* big enough for "QMan portal %d" */
+#define QMAN_POLL_LIMIT 32
+#define QMAN_PIRQ_DQRR_ITHRESH 12
+#define QMAN_PIRQ_MR_ITHRESH 4
+#define QMAN_PIRQ_IPERIOD 100
+
+/* Portal register assists */
+
+/* Cache-inhibited register offsets */
+#define QM_REG_EQCR_PI_CINH    0x0000
+#define QM_REG_EQCR_CI_CINH    0x0004
+#define QM_REG_EQCR_ITR                0x0008
+#define QM_REG_DQRR_PI_CINH    0x0040
+#define QM_REG_DQRR_CI_CINH    0x0044
+#define QM_REG_DQRR_ITR                0x0048
+#define QM_REG_DQRR_DCAP       0x0050
+#define QM_REG_DQRR_SDQCR      0x0054
+#define QM_REG_DQRR_VDQCR      0x0058
+#define QM_REG_DQRR_PDQCR      0x005c
+#define QM_REG_MR_PI_CINH      0x0080
+#define QM_REG_MR_CI_CINH      0x0084
+#define QM_REG_MR_ITR          0x0088
+#define QM_REG_CFG             0x0100
+#define QM_REG_ISR             0x0e00
+#define QM_REG_IER             0x0e04
+#define QM_REG_ISDR            0x0e08
+#define QM_REG_IIR             0x0e0c
+#define QM_REG_ITPR            0x0e14
+
+/* Cache-enabled register offsets */
+#define QM_CL_EQCR             0x0000
+#define QM_CL_DQRR             0x1000
+#define QM_CL_MR               0x2000
+#define QM_CL_EQCR_PI_CENA     0x3000
+#define QM_CL_EQCR_CI_CENA     0x3100
+#define QM_CL_DQRR_PI_CENA     0x3200
+#define QM_CL_DQRR_CI_CENA     0x3300
+#define QM_CL_MR_PI_CENA       0x3400
+#define QM_CL_MR_CI_CENA       0x3500
+#define QM_CL_CR               0x3800
+#define QM_CL_RR0              0x3900
+#define QM_CL_RR1              0x3940
+
+/*
+ * BTW, the drivers (and h/w programming model) already obtain the required
+ * synchronisation for portal accesses and data-dependencies. Use of barrier()s
+ * or other order-preserving primitives simply degrade performance. Hence the
+ * use of the __raw_*() interfaces, which simply ensure that the compiler treats
+ * the portal registers as volatile
+ */
+
+/* Cache-enabled ring access */
+#define qm_cl(base, idx)       ((void *)base + ((idx) << 6))
+
+/*
+ * Portal modes.
+ *   Enum types;
+ *     pmode == production mode
+ *     cmode == consumption mode,
+ *     dmode == h/w dequeue mode.
+ *   Enum values use 3 letter codes. First letter matches the portal mode,
+ *   remaining two letters indicate;
+ *     ci == cache-inhibited portal register
+ *     ce == cache-enabled portal register
+ *     vb == in-band valid-bit (cache-enabled)
+ *     dc == DCA (Discrete Consumption Acknowledgment), DQRR-only
+ *   As for "enum qm_dqrr_dmode", it should be self-explanatory.
+ */
+enum qm_eqcr_pmode {           /* matches QCSP_CFG::EPM */
+       qm_eqcr_pci = 0,        /* PI index, cache-inhibited */
+       qm_eqcr_pce = 1,        /* PI index, cache-enabled */
+       qm_eqcr_pvb = 2         /* valid-bit */
+};
+enum qm_dqrr_dmode {           /* matches QCSP_CFG::DP */
+       qm_dqrr_dpush = 0,      /* SDQCR  + VDQCR */
+       qm_dqrr_dpull = 1       /* PDQCR */
+};
+enum qm_dqrr_pmode {           /* s/w-only */
+       qm_dqrr_pci,            /* reads DQRR_PI_CINH */
+       qm_dqrr_pce,            /* reads DQRR_PI_CENA */
+       qm_dqrr_pvb             /* reads valid-bit */
+};
+enum qm_dqrr_cmode {           /* matches QCSP_CFG::DCM */
+       qm_dqrr_cci = 0,        /* CI index, cache-inhibited */
+       qm_dqrr_cce = 1,        /* CI index, cache-enabled */
+       qm_dqrr_cdc = 2         /* Discrete Consumption Acknowledgment */
+};
+enum qm_mr_pmode {             /* s/w-only */
+       qm_mr_pci,              /* reads MR_PI_CINH */
+       qm_mr_pce,              /* reads MR_PI_CENA */
+       qm_mr_pvb               /* reads valid-bit */
+};
+enum qm_mr_cmode {             /* matches QCSP_CFG::MM */
+       qm_mr_cci = 0,          /* CI index, cache-inhibited */
+       qm_mr_cce = 1           /* CI index, cache-enabled */
+};
+
+/* --- Portal structures --- */
+
+#define QM_EQCR_SIZE           8
+#define QM_DQRR_SIZE           16
+#define QM_MR_SIZE             8
+
+/* "Enqueue Command" */
+struct qm_eqcr_entry {
+       u8 _ncw_verb; /* writes to this are non-coherent */
+       u8 dca;
+       u16 seqnum;
+       u32 orp;        /* 24-bit */
+       u32 fqid;       /* 24-bit */
+       u32 tag;
+       struct qm_fd fd;
+       u8 __reserved3[32];
+} __packed;
+#define QM_EQCR_VERB_VBIT              0x80
+#define QM_EQCR_VERB_CMD_MASK          0x61    /* but only one value; */
+#define QM_EQCR_VERB_CMD_ENQUEUE       0x01
+#define QM_EQCR_SEQNUM_NESN            0x8000  /* Advance NESN */
+#define QM_EQCR_SEQNUM_NLIS            0x4000  /* More fragments to come */
+#define QM_EQCR_SEQNUM_SEQMASK         0x3fff  /* sequence number goes here */
+
+struct qm_eqcr {
+       struct qm_eqcr_entry *ring, *cursor;
+       u8 ci, available, ithresh, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       u32 busy;
+       enum qm_eqcr_pmode pmode;
+#endif
+};
+
+struct qm_dqrr {
+       const struct qm_dqrr_entry *ring, *cursor;
+       u8 pi, ci, fill, ithresh, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       enum qm_dqrr_dmode dmode;
+       enum qm_dqrr_pmode pmode;
+       enum qm_dqrr_cmode cmode;
+#endif
+};
+
+struct qm_mr {
+       union qm_mr_entry *ring, *cursor;
+       u8 pi, ci, fill, ithresh, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       enum qm_mr_pmode pmode;
+       enum qm_mr_cmode cmode;
+#endif
+};
+
+/* MC (Management Command) command */
+/* "Query FQ" */
+struct qm_mcc_queryfq {
+       u8 _ncw_verb;
+       u8 __reserved1[3];
+       u32 fqid;       /* 24-bit */
+       u8 __reserved2[56];
+} __packed;
+/* "Alter FQ State Commands " */
+struct qm_mcc_alterfq {
+       u8 _ncw_verb;
+       u8 __reserved1[3];
+       u32 fqid;       /* 24-bit */
+       u8 __reserved2;
+       u8 count;       /* number of consecutive FQID */
+       u8 __reserved3[10];
+       u32 context_b;  /* frame queue context b */
+       u8 __reserved4[40];
+} __packed;
+
+/* "Query CGR" */
+struct qm_mcc_querycgr {
+       u8 _ncw_verb;
+       u8 __reserved1[30];
+       u8 cgid;
+       u8 __reserved2[32];
+};
+
+struct qm_mcc_querywq {
+       u8 _ncw_verb;
+       u8 __reserved;
+       /* select channel if verb != QUERYWQ_DEDICATED */
+       u16 channel_wq; /* ignores wq (3 lsbits): _res[0-2] */
+       u8 __reserved2[60];
+} __packed;
+
+#define QM_MCC_VERB_VBIT               0x80
+#define QM_MCC_VERB_MASK               0x7f    /* where the verb contains; */
+#define QM_MCC_VERB_INITFQ_PARKED      0x40
+#define QM_MCC_VERB_INITFQ_SCHED       0x41
+#define QM_MCC_VERB_QUERYFQ            0x44
+#define QM_MCC_VERB_QUERYFQ_NP         0x45    /* "non-programmable" fields */
+#define QM_MCC_VERB_QUERYWQ            0x46
+#define QM_MCC_VERB_QUERYWQ_DEDICATED  0x47
+#define QM_MCC_VERB_ALTER_SCHED                0x48    /* Schedule FQ */
+#define QM_MCC_VERB_ALTER_FE           0x49    /* Force Eligible FQ */
+#define QM_MCC_VERB_ALTER_RETIRE       0x4a    /* Retire FQ */
+#define QM_MCC_VERB_ALTER_OOS          0x4b    /* Take FQ out of service */
+#define QM_MCC_VERB_ALTER_FQXON                0x4d    /* FQ XON */
+#define QM_MCC_VERB_ALTER_FQXOFF       0x4e    /* FQ XOFF */
+#define QM_MCC_VERB_INITCGR            0x50
+#define QM_MCC_VERB_MODIFYCGR          0x51
+#define QM_MCC_VERB_CGRTESTWRITE       0x52
+#define QM_MCC_VERB_QUERYCGR           0x58
+#define QM_MCC_VERB_QUERYCONGESTION    0x59
+union qm_mc_command {
+       struct {
+               u8 _ncw_verb; /* writes to this are non-coherent */
+               u8 __reserved[63];
+       };
+       struct qm_mcc_initfq initfq;
+       struct qm_mcc_queryfq queryfq;
+       struct qm_mcc_alterfq alterfq;
+       struct qm_mcc_initcgr initcgr;
+       struct qm_mcc_querycgr querycgr;
+       struct qm_mcc_querywq querywq;
+       struct qm_mcc_queryfq_np queryfq_np;
+};
+
+/* MC (Management Command) result */
+/* "Query FQ" */
+struct qm_mcr_queryfq {
+       u8 verb;
+       u8 result;
+       u8 __reserved1[8];
+       struct qm_fqd fqd;      /* the FQD fields are here */
+       u8 __reserved2[30];
+} __packed;
+
+/* "Alter FQ State Commands" */
+struct qm_mcr_alterfq {
+       u8 verb;
+       u8 result;
+       u8 fqs;         /* Frame Queue Status */
+       u8 __reserved1[61];
+};
+#define QM_MCR_VERB_RRID               0x80
+#define QM_MCR_VERB_MASK               QM_MCC_VERB_MASK
+#define QM_MCR_VERB_INITFQ_PARKED      QM_MCC_VERB_INITFQ_PARKED
+#define QM_MCR_VERB_INITFQ_SCHED       QM_MCC_VERB_INITFQ_SCHED
+#define QM_MCR_VERB_QUERYFQ            QM_MCC_VERB_QUERYFQ
+#define QM_MCR_VERB_QUERYFQ_NP         QM_MCC_VERB_QUERYFQ_NP
+#define QM_MCR_VERB_QUERYWQ            QM_MCC_VERB_QUERYWQ
+#define QM_MCR_VERB_QUERYWQ_DEDICATED  QM_MCC_VERB_QUERYWQ_DEDICATED
+#define QM_MCR_VERB_ALTER_SCHED                QM_MCC_VERB_ALTER_SCHED
+#define QM_MCR_VERB_ALTER_FE           QM_MCC_VERB_ALTER_FE
+#define QM_MCR_VERB_ALTER_RETIRE       QM_MCC_VERB_ALTER_RETIRE
+#define QM_MCR_VERB_ALTER_OOS          QM_MCC_VERB_ALTER_OOS
+#define QM_MCR_RESULT_NULL             0x00
+#define QM_MCR_RESULT_OK               0xf0
+#define QM_MCR_RESULT_ERR_FQID         0xf1
+#define QM_MCR_RESULT_ERR_FQSTATE      0xf2
+#define QM_MCR_RESULT_ERR_NOTEMPTY     0xf3    /* OOS fails if FQ is !empty */
+#define QM_MCR_RESULT_ERR_BADCHANNEL   0xf4
+#define QM_MCR_RESULT_PENDING          0xf8
+#define QM_MCR_RESULT_ERR_BADCOMMAND   0xff
+#define QM_MCR_FQS_ORLPRESENT          0x02    /* ORL fragments to come */
+#define QM_MCR_FQS_NOTEMPTY            0x01    /* FQ has enqueued frames */
+#define QM_MCR_TIMEOUT                 10000   /* us */
+union qm_mc_result {
+       struct {
+               u8 verb;
+               u8 result;
+               u8 __reserved1[62];
+       };
+       struct qm_mcr_queryfq queryfq;
+       struct qm_mcr_alterfq alterfq;
+       struct qm_mcr_querycgr querycgr;
+       struct qm_mcr_querycongestion querycongestion;
+       struct qm_mcr_querywq querywq;
+       struct qm_mcr_queryfq_np queryfq_np;
+};
+
+struct qm_mc {
+       union qm_mc_command *cr;
+       union qm_mc_result *rr;
+       u8 rridx, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       enum {
+               /* Can be _mc_start()ed */
+               qman_mc_idle,
+               /* Can be _mc_commit()ed or _mc_abort()ed */
+               qman_mc_user,
+               /* Can only be _mc_retry()ed */
+               qman_mc_hw
+       } state;
+#endif
+};
+
+struct qm_addr {
+       void __iomem *ce;       /* cache-enabled */
+       void __iomem *ci;       /* cache-inhibited */
+};
+
+struct qm_portal {
+       /*
+        * In the non-CONFIG_FSL_DPAA_CHECKING case, the following stuff up to
+        * and including 'mc' fits within a cacheline (yay!). The 'config' part
+        * is setup-only, so isn't a cause for a concern. In other words, don't
+        * rearrange this structure on a whim, there be dragons ...
+        */
+       struct qm_addr addr;
+       struct qm_eqcr eqcr;
+       struct qm_dqrr dqrr;
+       struct qm_mr mr;
+       struct qm_mc mc;
+} ____cacheline_aligned;
+
+/* Cache-inhibited register access. */
+static inline u32 qm_in(struct qm_portal *p, u32 offset)
+{
+       return __raw_readl(p->addr.ci + offset);
+}
+
+static inline void qm_out(struct qm_portal *p, u32 offset, u32 val)
+{
+       __raw_writel(val, p->addr.ci + offset);
+}
+
+/* Cache Enabled Portal Access */
+static inline void qm_cl_invalidate(struct qm_portal *p, u32 offset)
+{
+       dpaa_invalidate(p->addr.ce + offset);
+}
+
+static inline void qm_cl_touch_ro(struct qm_portal *p, u32 offset)
+{
+       dpaa_touch_ro(p->addr.ce + offset);
+}
+
+static inline u32 qm_ce_in(struct qm_portal *p, u32 offset)
+{
+       return __raw_readl(p->addr.ce + offset);
+}
+
+/* --- EQCR API --- */
+
+#define EQCR_SHIFT     ilog2(sizeof(struct qm_eqcr_entry))
+#define EQCR_CARRY     (uintptr_t)(QM_EQCR_SIZE << EQCR_SHIFT)
+
+/* Bit-wise logic to wrap a ring pointer by clearing the "carry bit" */
+static struct qm_eqcr_entry *eqcr_carryclear(struct qm_eqcr_entry *p)
+{
+       uintptr_t addr = (uintptr_t)p;
+
+       addr &= ~EQCR_CARRY;
+
+       return (struct qm_eqcr_entry *)addr;
+}
+
+/* Bit-wise logic to convert a ring pointer to a ring index */
+static int eqcr_ptr2idx(struct qm_eqcr_entry *e)
+{
+       return ((uintptr_t)e >> EQCR_SHIFT) & (QM_EQCR_SIZE - 1);
+}
+
+/* Increment the 'cursor' ring pointer, taking 'vbit' into account */
+static inline void eqcr_inc(struct qm_eqcr *eqcr)
+{
+       /* increment to the next EQCR pointer and handle overflow and 'vbit' */
+       struct qm_eqcr_entry *partial = eqcr->cursor + 1;
+
+       eqcr->cursor = eqcr_carryclear(partial);
+       if (partial != eqcr->cursor)
+               eqcr->vbit ^= QM_EQCR_VERB_VBIT;
+}
+
+static inline int qm_eqcr_init(struct qm_portal *portal,
+                               enum qm_eqcr_pmode pmode,
+                               unsigned int eq_stash_thresh,
+                               int eq_stash_prio)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+       u32 cfg;
+       u8 pi;
+
+       eqcr->ring = portal->addr.ce + QM_CL_EQCR;
+       eqcr->ci = qm_in(portal, QM_REG_EQCR_CI_CINH) & (QM_EQCR_SIZE - 1);
+       qm_cl_invalidate(portal, QM_CL_EQCR_CI_CENA);
+       pi = qm_in(portal, QM_REG_EQCR_PI_CINH) & (QM_EQCR_SIZE - 1);
+       eqcr->cursor = eqcr->ring + pi;
+       eqcr->vbit = (qm_in(portal, QM_REG_EQCR_PI_CINH) & QM_EQCR_SIZE) ?
+                    QM_EQCR_VERB_VBIT : 0;
+       eqcr->available = QM_EQCR_SIZE - 1 -
+                         dpaa_cyc_diff(QM_EQCR_SIZE, eqcr->ci, pi);
+       eqcr->ithresh = qm_in(portal, QM_REG_EQCR_ITR);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       eqcr->busy = 0;
+       eqcr->pmode = pmode;
+#endif
+       cfg = (qm_in(portal, QM_REG_CFG) & 0x00ffffff) |
+             (eq_stash_thresh << 28) | /* QCSP_CFG: EST */
+             (eq_stash_prio << 26) | /* QCSP_CFG: EP */
+             ((pmode & 0x3) << 24); /* QCSP_CFG::EPM */
+       qm_out(portal, QM_REG_CFG, cfg);
+       return 0;
+}
+
+static inline unsigned int qm_eqcr_get_ci_stashing(struct qm_portal *portal)
+{
+       return (qm_in(portal, QM_REG_CFG) >> 28) & 0x7;
+}
+
+static inline void qm_eqcr_finish(struct qm_portal *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+       u8 pi = qm_in(portal, QM_REG_EQCR_PI_CINH) & (QM_EQCR_SIZE - 1);
+       u8 ci = qm_in(portal, QM_REG_EQCR_CI_CINH) & (QM_EQCR_SIZE - 1);
+
+       DPAA_ASSERT(!eqcr->busy);
+       if (pi != eqcr_ptr2idx(eqcr->cursor))
+               pr_crit("losing uncommited EQCR entries\n");
+       if (ci != eqcr->ci)
+               pr_crit("missing existing EQCR completions\n");
+       if (eqcr->ci != eqcr_ptr2idx(eqcr->cursor))
+               pr_crit("EQCR destroyed unquiesced\n");
+}
+
+static inline struct qm_eqcr_entry *qm_eqcr_start_no_stash(struct qm_portal
+                                                                *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+
+       DPAA_ASSERT(!eqcr->busy);
+       if (!eqcr->available)
+               return NULL;
+
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       eqcr->busy = 1;
+#endif
+       dpaa_zero(eqcr->cursor);
+       return eqcr->cursor;
+}
+
+static inline struct qm_eqcr_entry *qm_eqcr_start_stash(struct qm_portal
+                                                               *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+       u8 diff, old_ci;
+
+       DPAA_ASSERT(!eqcr->busy);
+       if (!eqcr->available) {
+               old_ci = eqcr->ci;
+               eqcr->ci = qm_ce_in(portal, QM_CL_EQCR_CI_CENA) &
+                          (QM_EQCR_SIZE - 1);
+               diff = dpaa_cyc_diff(QM_EQCR_SIZE, old_ci, eqcr->ci);
+               eqcr->available += diff;
+               if (!diff)
+                       return NULL;
+       }
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       eqcr->busy = 1;
+#endif
+       dpaa_zero(eqcr->cursor);
+       return eqcr->cursor;
+}
+
+static inline void eqcr_commit_checks(struct qm_eqcr *eqcr)
+{
+       DPAA_ASSERT(eqcr->busy);
+       DPAA_ASSERT(eqcr->cursor->orp == (eqcr->cursor->orp & 0x00ffffff));
+       DPAA_ASSERT(eqcr->cursor->fqid == (eqcr->cursor->fqid & 0x00ffffff));
+       DPAA_ASSERT(eqcr->available >= 1);
+}
+
+static inline void qm_eqcr_pvb_commit(struct qm_portal *portal, u8 myverb)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+       struct qm_eqcr_entry *eqcursor;
+
+       eqcr_commit_checks(eqcr);
+       DPAA_ASSERT(eqcr->pmode == qm_eqcr_pvb);
+       dma_wmb();
+       eqcursor = eqcr->cursor;
+       eqcursor->_ncw_verb = myverb | eqcr->vbit;
+       dpaa_flush(eqcursor);
+       eqcr_inc(eqcr);
+       eqcr->available--;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       eqcr->busy = 0;
+#endif
+}
+
+static inline void qm_eqcr_cce_prefetch(struct qm_portal *portal)
+{
+       qm_cl_touch_ro(portal, QM_CL_EQCR_CI_CENA);
+}
+
+static inline u8 qm_eqcr_cce_update(struct qm_portal *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+       u8 diff, old_ci = eqcr->ci;
+
+       eqcr->ci = qm_ce_in(portal, QM_CL_EQCR_CI_CENA) & (QM_EQCR_SIZE - 1);
+       qm_cl_invalidate(portal, QM_CL_EQCR_CI_CENA);
+       diff = dpaa_cyc_diff(QM_EQCR_SIZE, old_ci, eqcr->ci);
+       eqcr->available += diff;
+       return diff;
+}
+
+static inline void qm_eqcr_set_ithresh(struct qm_portal *portal, u8 ithresh)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+
+       eqcr->ithresh = ithresh;
+       qm_out(portal, QM_REG_EQCR_ITR, ithresh);
+}
+
+static inline u8 qm_eqcr_get_avail(struct qm_portal *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+
+       return eqcr->available;
+}
+
+static inline u8 qm_eqcr_get_fill(struct qm_portal *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+
+       return QM_EQCR_SIZE - 1 - eqcr->available;
+}
+
+/* --- DQRR API --- */
+
+#define DQRR_SHIFT     ilog2(sizeof(struct qm_dqrr_entry))
+#define DQRR_CARRY     (uintptr_t)(QM_DQRR_SIZE << DQRR_SHIFT)
+
+static const struct qm_dqrr_entry *dqrr_carryclear(
+                                       const struct qm_dqrr_entry *p)
+{
+       uintptr_t addr = (uintptr_t)p;
+
+       addr &= ~DQRR_CARRY;
+
+       return (const struct qm_dqrr_entry *)addr;
+}
+
+static inline int dqrr_ptr2idx(const struct qm_dqrr_entry *e)
+{
+       return ((uintptr_t)e >> DQRR_SHIFT) & (QM_DQRR_SIZE - 1);
+}
+
+static const struct qm_dqrr_entry *dqrr_inc(const struct qm_dqrr_entry *e)
+{
+       return dqrr_carryclear(e + 1);
+}
+
+static inline void qm_dqrr_set_maxfill(struct qm_portal *portal, u8 mf)
+{
+       qm_out(portal, QM_REG_CFG, (qm_in(portal, QM_REG_CFG) & 0xff0fffff) |
+                                  ((mf & (QM_DQRR_SIZE - 1)) << 20));
+}
+
+static inline int qm_dqrr_init(struct qm_portal *portal,
+                              const struct qm_portal_config *config,
+                              enum qm_dqrr_dmode dmode,
+                              enum qm_dqrr_pmode pmode,
+                              enum qm_dqrr_cmode cmode, u8 max_fill)
+{
+       struct qm_dqrr *dqrr = &portal->dqrr;
+       u32 cfg;
+
+       /* Make sure the DQRR will be idle when we enable */
+       qm_out(portal, QM_REG_DQRR_SDQCR, 0);
+       qm_out(portal, QM_REG_DQRR_VDQCR, 0);
+       qm_out(portal, QM_REG_DQRR_PDQCR, 0);
+       dqrr->ring = portal->addr.ce + QM_CL_DQRR;
+       dqrr->pi = qm_in(portal, QM_REG_DQRR_PI_CINH) & (QM_DQRR_SIZE - 1);
+       dqrr->ci = qm_in(portal, QM_REG_DQRR_CI_CINH) & (QM_DQRR_SIZE - 1);
+       dqrr->cursor = dqrr->ring + dqrr->ci;
+       dqrr->fill = dpaa_cyc_diff(QM_DQRR_SIZE, dqrr->ci, dqrr->pi);
+       dqrr->vbit = (qm_in(portal, QM_REG_DQRR_PI_CINH) & QM_DQRR_SIZE) ?
+                       QM_DQRR_VERB_VBIT : 0;
+       dqrr->ithresh = qm_in(portal, QM_REG_DQRR_ITR);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       dqrr->dmode = dmode;
+       dqrr->pmode = pmode;
+       dqrr->cmode = cmode;
+#endif
+       /* Invalidate every ring entry before beginning */
+       for (cfg = 0; cfg < QM_DQRR_SIZE; cfg++)
+               dpaa_invalidate(qm_cl(dqrr->ring, cfg));
+       cfg = (qm_in(portal, QM_REG_CFG) & 0xff000f00) |
+               ((max_fill & (QM_DQRR_SIZE - 1)) << 20) | /* DQRR_MF */
+               ((dmode & 1) << 18) |                   /* DP */
+               ((cmode & 3) << 16) |                   /* DCM */
+               0xa0 |                                  /* RE+SE */
+               (0 ? 0x40 : 0) |                        /* Ignore RP */
+               (0 ? 0x10 : 0);                         /* Ignore SP */
+       qm_out(portal, QM_REG_CFG, cfg);
+       qm_dqrr_set_maxfill(portal, max_fill);
+       return 0;
+}
+
+static inline void qm_dqrr_finish(struct qm_portal *portal)
+{
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       struct qm_dqrr *dqrr = &portal->dqrr;
+
+       if (dqrr->cmode != qm_dqrr_cdc &&
+           dqrr->ci != dqrr_ptr2idx(dqrr->cursor))
+               pr_crit("Ignoring completed DQRR entries\n");
+#endif
+}
+
+static inline const struct qm_dqrr_entry *qm_dqrr_current(
+                                               struct qm_portal *portal)
+{
+       struct qm_dqrr *dqrr = &portal->dqrr;
+
+       if (!dqrr->fill)
+               return NULL;
+       return dqrr->cursor;
+}
+
+static inline u8 qm_dqrr_next(struct qm_portal *portal)
+{
+       struct qm_dqrr *dqrr = &portal->dqrr;
+
+       DPAA_ASSERT(dqrr->fill);
+       dqrr->cursor = dqrr_inc(dqrr->cursor);
+       return --dqrr->fill;
+}
+
+static inline void qm_dqrr_pvb_update(struct qm_portal *portal)
+{
+       struct qm_dqrr *dqrr = &portal->dqrr;
+       struct qm_dqrr_entry *res = qm_cl(dqrr->ring, dqrr->pi);
+
+       DPAA_ASSERT(dqrr->pmode == qm_dqrr_pvb);
+#ifndef CONFIG_FSL_PAMU
+       /*
+        * If PAMU is not available we need to invalidate the cache.
+        * When PAMU is available the cache is updated by stash
+        */
+       dpaa_invalidate_touch_ro(res);
+#endif
+       /*
+        *  when accessing 'verb', use __raw_readb() to ensure that compiler
+        * inlining doesn't try to optimise out "excess reads".
+        */
+       if ((__raw_readb(&res->verb) & QM_DQRR_VERB_VBIT) == dqrr->vbit) {
+               dqrr->pi = (dqrr->pi + 1) & (QM_DQRR_SIZE - 1);
+               if (!dqrr->pi)
+                       dqrr->vbit ^= QM_DQRR_VERB_VBIT;
+               dqrr->fill++;
+       }
+}
+
+static inline void qm_dqrr_cdc_consume_1ptr(struct qm_portal *portal,
+                                       const struct qm_dqrr_entry *dq,
+                                       int park)
+{
+       __maybe_unused struct qm_dqrr *dqrr = &portal->dqrr;
+       int idx = dqrr_ptr2idx(dq);
+
+       DPAA_ASSERT(dqrr->cmode == qm_dqrr_cdc);
+       DPAA_ASSERT((dqrr->ring + idx) == dq);
+       DPAA_ASSERT(idx < QM_DQRR_SIZE);
+       qm_out(portal, QM_REG_DQRR_DCAP, (0 << 8) | /* DQRR_DCAP::S */
+              ((park ? 1 : 0) << 6) |              /* DQRR_DCAP::PK */
+              idx);                                /* DQRR_DCAP::DCAP_CI */
+}
+
+static inline void qm_dqrr_cdc_consume_n(struct qm_portal *portal, u32 bitmask)
+{
+       __maybe_unused struct qm_dqrr *dqrr = &portal->dqrr;
+
+       DPAA_ASSERT(dqrr->cmode == qm_dqrr_cdc);
+       qm_out(portal, QM_REG_DQRR_DCAP, (1 << 8) | /* DQRR_DCAP::S */
+              (bitmask << 16));                    /* DQRR_DCAP::DCAP_CI */
+}
+
+static inline void qm_dqrr_sdqcr_set(struct qm_portal *portal, u32 sdqcr)
+{
+       qm_out(portal, QM_REG_DQRR_SDQCR, sdqcr);
+}
+
+static inline void qm_dqrr_vdqcr_set(struct qm_portal *portal, u32 vdqcr)
+{
+       qm_out(portal, QM_REG_DQRR_VDQCR, vdqcr);
+}
+
+static inline void qm_dqrr_set_ithresh(struct qm_portal *portal, u8 ithresh)
+{
+       qm_out(portal, QM_REG_DQRR_ITR, ithresh);
+}
+
+/* --- MR API --- */
+
+#define MR_SHIFT       ilog2(sizeof(union qm_mr_entry))
+#define MR_CARRY       (uintptr_t)(QM_MR_SIZE << MR_SHIFT)
+
+static union qm_mr_entry *mr_carryclear(union qm_mr_entry *p)
+{
+       uintptr_t addr = (uintptr_t)p;
+
+       addr &= ~MR_CARRY;
+
+       return (union qm_mr_entry *)addr;
+}
+
+static inline int mr_ptr2idx(const union qm_mr_entry *e)
+{
+       return ((uintptr_t)e >> MR_SHIFT) & (QM_MR_SIZE - 1);
+}
+
+static inline union qm_mr_entry *mr_inc(union qm_mr_entry *e)
+{
+       return mr_carryclear(e + 1);
+}
+
+static inline int qm_mr_init(struct qm_portal *portal, enum qm_mr_pmode pmode,
+                            enum qm_mr_cmode cmode)
+{
+       struct qm_mr *mr = &portal->mr;
+       u32 cfg;
+
+       mr->ring = portal->addr.ce + QM_CL_MR;
+       mr->pi = qm_in(portal, QM_REG_MR_PI_CINH) & (QM_MR_SIZE - 1);
+       mr->ci = qm_in(portal, QM_REG_MR_CI_CINH) & (QM_MR_SIZE - 1);
+       mr->cursor = mr->ring + mr->ci;
+       mr->fill = dpaa_cyc_diff(QM_MR_SIZE, mr->ci, mr->pi);
+       mr->vbit = (qm_in(portal, QM_REG_MR_PI_CINH) & QM_MR_SIZE)
+               ? QM_MR_VERB_VBIT : 0;
+       mr->ithresh = qm_in(portal, QM_REG_MR_ITR);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mr->pmode = pmode;
+       mr->cmode = cmode;
+#endif
+       cfg = (qm_in(portal, QM_REG_CFG) & 0xfffff0ff) |
+             ((cmode & 1) << 8);       /* QCSP_CFG:MM */
+       qm_out(portal, QM_REG_CFG, cfg);
+       return 0;
+}
+
+static inline void qm_mr_finish(struct qm_portal *portal)
+{
+       struct qm_mr *mr = &portal->mr;
+
+       if (mr->ci != mr_ptr2idx(mr->cursor))
+               pr_crit("Ignoring completed MR entries\n");
+}
+
+static inline const union qm_mr_entry *qm_mr_current(struct qm_portal *portal)
+{
+       struct qm_mr *mr = &portal->mr;
+
+       if (!mr->fill)
+               return NULL;
+       return mr->cursor;
+}
+
+static inline int qm_mr_next(struct qm_portal *portal)
+{
+       struct qm_mr *mr = &portal->mr;
+
+       DPAA_ASSERT(mr->fill);
+       mr->cursor = mr_inc(mr->cursor);
+       return --mr->fill;
+}
+
+static inline void qm_mr_pvb_update(struct qm_portal *portal)
+{
+       struct qm_mr *mr = &portal->mr;
+       union qm_mr_entry *res = qm_cl(mr->ring, mr->pi);
+
+       DPAA_ASSERT(mr->pmode == qm_mr_pvb);
+       /*
+        *  when accessing 'verb', use __raw_readb() to ensure that compiler
+        * inlining doesn't try to optimise out "excess reads".
+        */
+       if ((__raw_readb(&res->verb) & QM_MR_VERB_VBIT) == mr->vbit) {
+               mr->pi = (mr->pi + 1) & (QM_MR_SIZE - 1);
+               if (!mr->pi)
+                       mr->vbit ^= QM_MR_VERB_VBIT;
+               mr->fill++;
+               res = mr_inc(res);
+       }
+       dpaa_invalidate_touch_ro(res);
+}
+
+static inline void qm_mr_cci_consume(struct qm_portal *portal, u8 num)
+{
+       struct qm_mr *mr = &portal->mr;
+
+       DPAA_ASSERT(mr->cmode == qm_mr_cci);
+       mr->ci = (mr->ci + num) & (QM_MR_SIZE - 1);
+       qm_out(portal, QM_REG_MR_CI_CINH, mr->ci);
+}
+
+static inline void qm_mr_cci_consume_to_current(struct qm_portal *portal)
+{
+       struct qm_mr *mr = &portal->mr;
+
+       DPAA_ASSERT(mr->cmode == qm_mr_cci);
+       mr->ci = mr_ptr2idx(mr->cursor);
+       qm_out(portal, QM_REG_MR_CI_CINH, mr->ci);
+}
+
+static inline void qm_mr_set_ithresh(struct qm_portal *portal, u8 ithresh)
+{
+       qm_out(portal, QM_REG_MR_ITR, ithresh);
+}
+
+/* --- Management command API --- */
+
+static inline int qm_mc_init(struct qm_portal *portal)
+{
+       struct qm_mc *mc = &portal->mc;
+
+       mc->cr = portal->addr.ce + QM_CL_CR;
+       mc->rr = portal->addr.ce + QM_CL_RR0;
+       mc->rridx = (__raw_readb(&mc->cr->_ncw_verb) & QM_MCC_VERB_VBIT)
+                   ? 0 : 1;
+       mc->vbit = mc->rridx ? QM_MCC_VERB_VBIT : 0;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = qman_mc_idle;
+#endif
+       return 0;
+}
+
+static inline void qm_mc_finish(struct qm_portal *portal)
+{
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       struct qm_mc *mc = &portal->mc;
+
+       DPAA_ASSERT(mc->state == qman_mc_idle);
+       if (mc->state != qman_mc_idle)
+               pr_crit("Losing incomplete MC command\n");
+#endif
+}
+
+static inline union qm_mc_command *qm_mc_start(struct qm_portal *portal)
+{
+       struct qm_mc *mc = &portal->mc;
+
+       DPAA_ASSERT(mc->state == qman_mc_idle);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = qman_mc_user;
+#endif
+       dpaa_zero(mc->cr);
+       return mc->cr;
+}
+
+static inline void qm_mc_commit(struct qm_portal *portal, u8 myverb)
+{
+       struct qm_mc *mc = &portal->mc;
+       union qm_mc_result *rr = mc->rr + mc->rridx;
+
+       DPAA_ASSERT(mc->state == qman_mc_user);
+       dma_wmb();
+       mc->cr->_ncw_verb = myverb | mc->vbit;
+       dpaa_flush(mc->cr);
+       dpaa_invalidate_touch_ro(rr);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = qman_mc_hw;
+#endif
+}
+
+static inline union qm_mc_result *qm_mc_result(struct qm_portal *portal)
+{
+       struct qm_mc *mc = &portal->mc;
+       union qm_mc_result *rr = mc->rr + mc->rridx;
+
+       DPAA_ASSERT(mc->state == qman_mc_hw);
+       /*
+        *  The inactive response register's verb byte always returns zero until
+        * its command is submitted and completed. This includes the valid-bit,
+        * in case you were wondering...
+        */
+       if (!__raw_readb(&rr->verb)) {
+               dpaa_invalidate_touch_ro(rr);
+               return NULL;
+       }
+       mc->rridx ^= 1;
+       mc->vbit ^= QM_MCC_VERB_VBIT;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = qman_mc_idle;
+#endif
+       return rr;
+}
+
+static inline int qm_mc_result_timeout(struct qm_portal *portal,
+                                      union qm_mc_result **mcr)
+{
+       int timeout = QM_MCR_TIMEOUT;
+
+       do {
+               *mcr = qm_mc_result(portal);
+               if (*mcr)
+                       break;
+               udelay(1);
+       } while (--timeout);
+
+       return timeout;
+}
+
+static inline void fq_set(struct qman_fq *fq, u32 mask)
+{
+       set_bits(mask, &fq->flags);
+}
+
+static inline void fq_clear(struct qman_fq *fq, u32 mask)
+{
+       clear_bits(mask, &fq->flags);
+}
+
+static inline int fq_isset(struct qman_fq *fq, u32 mask)
+{
+       return fq->flags & mask;
+}
+
+static inline int fq_isclear(struct qman_fq *fq, u32 mask)
+{
+       return !(fq->flags & mask);
+}
+
+struct qman_portal {
+       struct qm_portal p;
+       /* PORTAL_BITS_*** - dynamic, strictly internal */
+       unsigned long bits;
+       /* interrupt sources processed by portal_isr(), configurable */
+       unsigned long irq_sources;
+       u32 use_eqcr_ci_stashing;
+       /* only 1 volatile dequeue at a time */
+       struct qman_fq *vdqcr_owned;
+       u32 sdqcr;
+       /* probing time config params for cpu-affine portals */
+       const struct qm_portal_config *config;
+       /* needed for providing a non-NULL device to dma_map_***() */
+       struct platform_device *pdev;
+       /* 2-element array. cgrs[0] is mask, cgrs[1] is snapshot. */
+       struct qman_cgrs *cgrs;
+       /* linked-list of CSCN handlers. */
+       struct list_head cgr_cbs;
+       /* list lock */
+       spinlock_t cgr_lock;
+       struct work_struct congestion_work;
+       struct work_struct mr_work;
+       char irqname[MAX_IRQNAME];
+};
+
+static cpumask_t affine_mask;
+static DEFINE_SPINLOCK(affine_mask_lock);
+static u16 affine_channels[NR_CPUS];
+static DEFINE_PER_CPU(struct qman_portal, qman_affine_portal);
+struct qman_portal *affine_portals[NR_CPUS];
+
+static inline struct qman_portal *get_affine_portal(void)
+{
+       return &get_cpu_var(qman_affine_portal);
+}
+
+static inline void put_affine_portal(void)
+{
+       put_cpu_var(qman_affine_portal);
+}
+
+static struct workqueue_struct *qm_portal_wq;
+
+int qman_wq_alloc(void)
+{
+       qm_portal_wq = alloc_workqueue("qman_portal_wq", 0, 1);
+       if (!qm_portal_wq)
+               return -ENOMEM;
+       return 0;
+}
+
+/*
+ * This is what everything can wait on, even if it migrates to a different cpu
+ * to the one whose affine portal it is waiting on.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(affine_queue);
+
+static struct qman_fq **fq_table;
+static u32 num_fqids;
+
+int qman_alloc_fq_table(u32 _num_fqids)
+{
+       num_fqids = _num_fqids;
+
+       fq_table = vzalloc(num_fqids * 2 * sizeof(struct qman_fq *));
+       if (!fq_table)
+               return -ENOMEM;
+
+       pr_debug("Allocated fq lookup table at %p, entry count %u\n",
+                fq_table, num_fqids * 2);
+       return 0;
+}
+
+static struct qman_fq *idx_to_fq(u32 idx)
+{
+       struct qman_fq *fq;
+
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       if (WARN_ON(idx >= num_fqids * 2))
+               return NULL;
+#endif
+       fq = fq_table[idx];
+       DPAA_ASSERT(!fq || idx == fq->idx);
+
+       return fq;
+}
+
+/*
+ * Only returns full-service fq objects, not enqueue-only
+ * references (QMAN_FQ_FLAG_NO_MODIFY).
+ */
+static struct qman_fq *fqid_to_fq(u32 fqid)
+{
+       return idx_to_fq(fqid * 2);
+}
+
+static struct qman_fq *tag_to_fq(u32 tag)
+{
+#if BITS_PER_LONG == 64
+       return idx_to_fq(tag);
+#else
+       return (struct qman_fq *)tag;
+#endif
+}
+
+static u32 fq_to_tag(struct qman_fq *fq)
+{
+#if BITS_PER_LONG == 64
+       return fq->idx;
+#else
+       return (u32)fq;
+#endif
+}
+
+static u32 __poll_portal_slow(struct qman_portal *p, u32 is);
+static inline unsigned int __poll_portal_fast(struct qman_portal *p,
+                                       unsigned int poll_limit);
+static void qm_congestion_task(struct work_struct *work);
+static void qm_mr_process_task(struct work_struct *work);
+
+static irqreturn_t portal_isr(int irq, void *ptr)
+{
+       struct qman_portal *p = ptr;
+
+       u32 clear = QM_DQAVAIL_MASK | p->irq_sources;
+       u32 is = qm_in(&p->p, QM_REG_ISR) & p->irq_sources;
+
+       if (unlikely(!is))
+               return IRQ_NONE;
+
+       /* DQRR-handling if it's interrupt-driven */
+       if (is & QM_PIRQ_DQRI)
+               __poll_portal_fast(p, QMAN_POLL_LIMIT);
+       /* Handling of anything else that's interrupt-driven */
+       clear |= __poll_portal_slow(p, is);
+       qm_out(&p->p, QM_REG_ISR, clear);
+       return IRQ_HANDLED;
+}
+
+static int drain_mr_fqrni(struct qm_portal *p)
+{
+       const union qm_mr_entry *msg;
+loop:
+       msg = qm_mr_current(p);
+       if (!msg) {
+               /*
+                * if MR was full and h/w had other FQRNI entries to produce, we
+                * need to allow it time to produce those entries once the
+                * existing entries are consumed. A worst-case situation
+                * (fully-loaded system) means h/w sequencers may have to do 3-4
+                * other things before servicing the portal's MR pump, each of
+                * which (if slow) may take ~50 qman cycles (which is ~200
+                * processor cycles). So rounding up and then multiplying this
+                * worst-case estimate by a factor of 10, just to be
+                * ultra-paranoid, goes as high as 10,000 cycles. NB, we consume
+                * one entry at a time, so h/w has an opportunity to produce new
+                * entries well before the ring has been fully consumed, so
+                * we're being *really* paranoid here.
+                */
+               u64 now, then = jiffies;
+
+               do {
+                       now = jiffies;
+               } while ((then + 10000) > now);
+               msg = qm_mr_current(p);
+               if (!msg)
+                       return 0;
+       }
+       if ((msg->verb & QM_MR_VERB_TYPE_MASK) != QM_MR_VERB_FQRNI) {
+               /* We aren't draining anything but FQRNIs */
+               pr_err("Found verb 0x%x in MR\n", msg->verb);
+               return -1;
+       }
+       qm_mr_next(p);
+       qm_mr_cci_consume(p, 1);
+       goto loop;
+}
+
+static int qman_create_portal(struct qman_portal *portal,
+                             const struct qm_portal_config *c,
+                             const struct qman_cgrs *cgrs)
+{
+       struct qm_portal *p;
+       char buf[16];
+       int ret;
+       u32 isdr;
+
+       p = &portal->p;
+
+#ifdef CONFIG_FSL_PAMU
+       /* PAMU is required for stashing */
+       portal->use_eqcr_ci_stashing = ((qman_ip_rev >= QMAN_REV30) ? 1 : 0);
+#else
+       portal->use_eqcr_ci_stashing = 0;
+#endif
+       /*
+        * prep the low-level portal struct with the mapped addresses from the
+        * config, everything that follows depends on it and "config" is more
+        * for (de)reference
+        */
+       p->addr.ce = c->addr_virt[DPAA_PORTAL_CE];
+       p->addr.ci = c->addr_virt[DPAA_PORTAL_CI];
+       /*
+        * If CI-stashing is used, the current defaults use a threshold of 3,
+        * and stash with high-than-DQRR priority.
+        */
+       if (qm_eqcr_init(p, qm_eqcr_pvb,
+                       portal->use_eqcr_ci_stashing ? 3 : 0, 1)) {
+               dev_err(c->dev, "EQCR initialisation failed\n");
+               goto fail_eqcr;
+       }
+       if (qm_dqrr_init(p, c, qm_dqrr_dpush, qm_dqrr_pvb,
+                       qm_dqrr_cdc, DQRR_MAXFILL)) {
+               dev_err(c->dev, "DQRR initialisation failed\n");
+               goto fail_dqrr;
+       }
+       if (qm_mr_init(p, qm_mr_pvb, qm_mr_cci)) {
+               dev_err(c->dev, "MR initialisation failed\n");
+               goto fail_mr;
+       }
+       if (qm_mc_init(p)) {
+               dev_err(c->dev, "MC initialisation failed\n");
+               goto fail_mc;
+       }
+       /* static interrupt-gating controls */
+       qm_dqrr_set_ithresh(p, QMAN_PIRQ_DQRR_ITHRESH);
+       qm_mr_set_ithresh(p, QMAN_PIRQ_MR_ITHRESH);
+       qm_out(p, QM_REG_ITPR, QMAN_PIRQ_IPERIOD);
+       portal->cgrs = kmalloc(2 * sizeof(*cgrs), GFP_KERNEL);
+       if (!portal->cgrs)
+               goto fail_cgrs;
+       /* initial snapshot is no-depletion */
+       qman_cgrs_init(&portal->cgrs[1]);
+       if (cgrs)
+               portal->cgrs[0] = *cgrs;
+       else
+               /* if the given mask is NULL, assume all CGRs can be seen */
+               qman_cgrs_fill(&portal->cgrs[0]);
+       INIT_LIST_HEAD(&portal->cgr_cbs);
+       spin_lock_init(&portal->cgr_lock);
+       INIT_WORK(&portal->congestion_work, qm_congestion_task);
+       INIT_WORK(&portal->mr_work, qm_mr_process_task);
+       portal->bits = 0;
+       portal->sdqcr = QM_SDQCR_SOURCE_CHANNELS | QM_SDQCR_COUNT_UPTO3 |
+                       QM_SDQCR_DEDICATED_PRECEDENCE | QM_SDQCR_TYPE_PRIO_QOS |
+                       QM_SDQCR_TOKEN_SET(0xab) | QM_SDQCR_CHANNELS_DEDICATED;
+       sprintf(buf, "qportal-%d", c->channel);
+       portal->pdev = platform_device_alloc(buf, -1);
+       if (!portal->pdev)
+               goto fail_devalloc;
+       if (dma_set_mask(&portal->pdev->dev, DMA_BIT_MASK(40)))
+               goto fail_devadd;
+       ret = platform_device_add(portal->pdev);
+       if (ret)
+               goto fail_devadd;
+       isdr = 0xffffffff;
+       qm_out(p, QM_REG_ISDR, isdr);
+       portal->irq_sources = 0;
+       qm_out(p, QM_REG_IER, 0);
+       qm_out(p, QM_REG_ISR, 0xffffffff);
+       snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, c->cpu);
+       if (request_irq(c->irq, portal_isr, 0, portal->irqname, portal)) {
+               dev_err(c->dev, "request_irq() failed\n");
+               goto fail_irq;
+       }
+       if (c->cpu != -1 && irq_can_set_affinity(c->irq) &&
+           irq_set_affinity(c->irq, cpumask_of(c->cpu))) {
+               dev_err(c->dev, "irq_set_affinity() failed\n");
+               goto fail_affinity;
+       }
+
+       /* Need EQCR to be empty before continuing */
+       isdr &= ~QM_PIRQ_EQCI;
+       qm_out(p, QM_REG_ISDR, isdr);
+       ret = qm_eqcr_get_fill(p);
+       if (ret) {
+               dev_err(c->dev, "EQCR unclean\n");
+               goto fail_eqcr_empty;
+       }
+       isdr &= ~(QM_PIRQ_DQRI | QM_PIRQ_MRI);
+       qm_out(p, QM_REG_ISDR, isdr);
+       if (qm_dqrr_current(p)) {
+               dev_err(c->dev, "DQRR unclean\n");
+               qm_dqrr_cdc_consume_n(p, 0xffff);
+       }
+       if (qm_mr_current(p) && drain_mr_fqrni(p)) {
+               /* special handling, drain just in case it's a few FQRNIs */
+               const union qm_mr_entry *e = qm_mr_current(p);
+
+               dev_err(c->dev, "MR dirty, VB 0x%x, rc 0x%x\n, addr 0x%x",
+                       e->verb, e->ern.rc, e->ern.fd.addr_lo);
+               goto fail_dqrr_mr_empty;
+       }
+       /* Success */
+       portal->config = c;
+       qm_out(p, QM_REG_ISDR, 0);
+       qm_out(p, QM_REG_IIR, 0);
+       /* Write a sane SDQCR */
+       qm_dqrr_sdqcr_set(p, portal->sdqcr);
+       return 0;
+
+fail_dqrr_mr_empty:
+fail_eqcr_empty:
+fail_affinity:
+       free_irq(c->irq, portal);
+fail_irq:
+       platform_device_del(portal->pdev);
+fail_devadd:
+       platform_device_put(portal->pdev);
+fail_devalloc:
+       kfree(portal->cgrs);
+fail_cgrs:
+       qm_mc_finish(p);
+fail_mc:
+       qm_mr_finish(p);
+fail_mr:
+       qm_dqrr_finish(p);
+fail_dqrr:
+       qm_eqcr_finish(p);
+fail_eqcr:
+       return -EIO;
+}
+
+struct qman_portal *qman_create_affine_portal(const struct qm_portal_config *c,
+                                             const struct qman_cgrs *cgrs)
+{
+       struct qman_portal *portal;
+       int err;
+
+       portal = &per_cpu(qman_affine_portal, c->cpu);
+       err = qman_create_portal(portal, c, cgrs);
+       if (err)
+               return NULL;
+
+       spin_lock(&affine_mask_lock);
+       cpumask_set_cpu(c->cpu, &affine_mask);
+       affine_channels[c->cpu] = c->channel;
+       affine_portals[c->cpu] = portal;
+       spin_unlock(&affine_mask_lock);
+
+       return portal;
+}
+
+static void qman_destroy_portal(struct qman_portal *qm)
+{
+       const struct qm_portal_config *pcfg;
+
+       /* Stop dequeues on the portal */
+       qm_dqrr_sdqcr_set(&qm->p, 0);
+
+       /*
+        * NB we do this to "quiesce" EQCR. If we add enqueue-completions or
+        * something related to QM_PIRQ_EQCI, this may need fixing.
+        * Also, due to the prefetching model used for CI updates in the enqueue
+        * path, this update will only invalidate the CI cacheline *after*
+        * working on it, so we need to call this twice to ensure a full update
+        * irrespective of where the enqueue processing was at when the teardown
+        * began.
+        */
+       qm_eqcr_cce_update(&qm->p);
+       qm_eqcr_cce_update(&qm->p);
+       pcfg = qm->config;
+
+       free_irq(pcfg->irq, qm);
+
+       kfree(qm->cgrs);
+       qm_mc_finish(&qm->p);
+       qm_mr_finish(&qm->p);
+       qm_dqrr_finish(&qm->p);
+       qm_eqcr_finish(&qm->p);
+
+       platform_device_del(qm->pdev);
+       platform_device_put(qm->pdev);
+
+       qm->config = NULL;
+}
+
+const struct qm_portal_config *qman_destroy_affine_portal(void)
+{
+       struct qman_portal *qm = get_affine_portal();
+       const struct qm_portal_config *pcfg;
+       int cpu;
+
+       pcfg = qm->config;
+       cpu = pcfg->cpu;
+
+       qman_destroy_portal(qm);
+
+       spin_lock(&affine_mask_lock);
+       cpumask_clear_cpu(cpu, &affine_mask);
+       spin_unlock(&affine_mask_lock);
+       put_affine_portal();
+       return pcfg;
+}
+
+/* Inline helper to reduce nesting in __poll_portal_slow() */
+static inline void fq_state_change(struct qman_portal *p, struct qman_fq *fq,
+                                  const union qm_mr_entry *msg, u8 verb)
+{
+       switch (verb) {
+       case QM_MR_VERB_FQRL:
+               DPAA_ASSERT(fq_isset(fq, QMAN_FQ_STATE_ORL));
+               fq_clear(fq, QMAN_FQ_STATE_ORL);
+               break;
+       case QM_MR_VERB_FQRN:
+               DPAA_ASSERT(fq->state == qman_fq_state_parked ||
+                           fq->state == qman_fq_state_sched);
+               DPAA_ASSERT(fq_isset(fq, QMAN_FQ_STATE_CHANGING));
+               fq_clear(fq, QMAN_FQ_STATE_CHANGING);
+               if (msg->fq.fqs & QM_MR_FQS_NOTEMPTY)
+                       fq_set(fq, QMAN_FQ_STATE_NE);
+               if (msg->fq.fqs & QM_MR_FQS_ORLPRESENT)
+                       fq_set(fq, QMAN_FQ_STATE_ORL);
+               fq->state = qman_fq_state_retired;
+               break;
+       case QM_MR_VERB_FQPN:
+               DPAA_ASSERT(fq->state == qman_fq_state_sched);
+               DPAA_ASSERT(fq_isclear(fq, QMAN_FQ_STATE_CHANGING));
+               fq->state = qman_fq_state_parked;
+       }
+}
+
+static void qm_congestion_task(struct work_struct *work)
+{
+       struct qman_portal *p = container_of(work, struct qman_portal,
+                                            congestion_work);
+       struct qman_cgrs rr, c;
+       union qm_mc_result *mcr;
+       struct qman_cgr *cgr;
+
+       spin_lock(&p->cgr_lock);
+       qm_mc_start(&p->p);
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCONGESTION);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               spin_unlock(&p->cgr_lock);
+               dev_crit(p->config->dev, "QUERYCONGESTION timeout\n");
+               return;
+       }
+       /* mask out the ones I'm not interested in */
+       qman_cgrs_and(&rr, (struct qman_cgrs *)&mcr->querycongestion.state,
+                     &p->cgrs[0]);
+       /* check previous snapshot for delta, enter/exit congestion */
+       qman_cgrs_xor(&c, &rr, &p->cgrs[1]);
+       /* update snapshot */
+       qman_cgrs_cp(&p->cgrs[1], &rr);
+       /* Invoke callback */
+       list_for_each_entry(cgr, &p->cgr_cbs, node)
+               if (cgr->cb && qman_cgrs_get(&c, cgr->cgrid))
+                       cgr->cb(p, cgr, qman_cgrs_get(&rr, cgr->cgrid));
+       spin_unlock(&p->cgr_lock);
+}
+
+static void qm_mr_process_task(struct work_struct *work)
+{
+       struct qman_portal *p = container_of(work, struct qman_portal,
+                                            mr_work);
+       const union qm_mr_entry *msg;
+       struct qman_fq *fq;
+       u8 verb, num = 0;
+
+       preempt_disable();
+
+       while (1) {
+               qm_mr_pvb_update(&p->p);
+               msg = qm_mr_current(&p->p);
+               if (!msg)
+                       break;
+
+               verb = msg->verb & QM_MR_VERB_TYPE_MASK;
+               /* The message is a software ERN iff the 0x20 bit is clear */
+               if (verb & 0x20) {
+                       switch (verb) {
+                       case QM_MR_VERB_FQRNI:
+                               /* nada, we drop FQRNIs on the floor */
+                               break;
+                       case QM_MR_VERB_FQRN:
+                       case QM_MR_VERB_FQRL:
+                               /* Lookup in the retirement table */
+                               fq = fqid_to_fq(msg->fq.fqid);
+                               if (WARN_ON(!fq))
+                                       break;
+                               fq_state_change(p, fq, msg, verb);
+                               if (fq->cb.fqs)
+                                       fq->cb.fqs(p, fq, msg);
+                               break;
+                       case QM_MR_VERB_FQPN:
+                               /* Parked */
+                               fq = tag_to_fq(msg->fq.contextB);
+                               fq_state_change(p, fq, msg, verb);
+                               if (fq->cb.fqs)
+                                       fq->cb.fqs(p, fq, msg);
+                               break;
+                       case QM_MR_VERB_DC_ERN:
+                               /* DCP ERN */
+                               pr_crit_once("Leaking DCP ERNs!\n");
+                               break;
+                       default:
+                               pr_crit("Invalid MR verb 0x%02x\n", verb);
+                       }
+               } else {
+                       /* Its a software ERN */
+                       fq = tag_to_fq(msg->ern.tag);
+                       fq->cb.ern(p, fq, msg);
+               }
+               num++;
+               qm_mr_next(&p->p);
+       }
+
+       qm_mr_cci_consume(&p->p, num);
+       preempt_enable();
+}
+
+static u32 __poll_portal_slow(struct qman_portal *p, u32 is)
+{
+       if (is & QM_PIRQ_CSCI) {
+               queue_work_on(smp_processor_id(), qm_portal_wq,
+                             &p->congestion_work);
+       }
+
+       if (is & QM_PIRQ_EQRI) {
+               qm_eqcr_cce_update(&p->p);
+               qm_eqcr_set_ithresh(&p->p, 0);
+               wake_up(&affine_queue);
+       }
+
+       if (is & QM_PIRQ_MRI) {
+               queue_work_on(smp_processor_id(), qm_portal_wq,
+                             &p->mr_work);
+       }
+
+       return is;
+}
+
+/*
+ * remove some slowish-path stuff from the "fast path" and make sure it isn't
+ * inlined.
+ */
+static noinline void clear_vdqcr(struct qman_portal *p, struct qman_fq *fq)
+{
+       p->vdqcr_owned = NULL;
+       fq_clear(fq, QMAN_FQ_STATE_VDQCR);
+       wake_up(&affine_queue);
+}
+
+/*
+ * The only states that would conflict with other things if they ran at the
+ * same time on the same cpu are:
+ *
+ *   (i) setting/clearing vdqcr_owned, and
+ *  (ii) clearing the NE (Not Empty) flag.
+ *
+ * Both are safe. Because;
+ *
+ *   (i) this clearing can only occur after qman_volatile_dequeue() has set the
+ *      vdqcr_owned field (which it does before setting VDQCR), and
+ *      qman_volatile_dequeue() blocks interrupts and preemption while this is
+ *      done so that we can't interfere.
+ *  (ii) the NE flag is only cleared after qman_retire_fq() has set it, and as
+ *      with (i) that API prevents us from interfering until it's safe.
+ *
+ * The good thing is that qman_volatile_dequeue() and qman_retire_fq() run far
+ * less frequently (ie. per-FQ) than __poll_portal_fast() does, so the nett
+ * advantage comes from this function not having to "lock" anything at all.
+ *
+ * Note also that the callbacks are invoked at points which are safe against the
+ * above potential conflicts, but that this function itself is not re-entrant
+ * (this is because the function tracks one end of each FIFO in the portal and
+ * we do *not* want to lock that). So the consequence is that it is safe for
+ * user callbacks to call into any QMan API.
+ */
+static inline unsigned int __poll_portal_fast(struct qman_portal *p,
+                                       unsigned int poll_limit)
+{
+       const struct qm_dqrr_entry *dq;
+       struct qman_fq *fq;
+       enum qman_cb_dqrr_result res;
+       unsigned int limit = 0;
+
+       do {
+               qm_dqrr_pvb_update(&p->p);
+               dq = qm_dqrr_current(&p->p);
+               if (!dq)
+                       break;
+
+               if (dq->stat & QM_DQRR_STAT_UNSCHEDULED) {
+                       /*
+                        * VDQCR: don't trust contextB as the FQ may have
+                        * been configured for h/w consumption and we're
+                        * draining it post-retirement.
+                        */
+                       fq = p->vdqcr_owned;
+                       /*
+                        * We only set QMAN_FQ_STATE_NE when retiring, so we
+                        * only need to check for clearing it when doing
+                        * volatile dequeues.  It's one less thing to check
+                        * in the critical path (SDQCR).
+                        */
+                       if (dq->stat & QM_DQRR_STAT_FQ_EMPTY)
+                               fq_clear(fq, QMAN_FQ_STATE_NE);
+                       /*
+                        * This is duplicated from the SDQCR code, but we
+                        * have stuff to do before *and* after this callback,
+                        * and we don't want multiple if()s in the critical
+                        * path (SDQCR).
+                        */
+                       res = fq->cb.dqrr(p, fq, dq);
+                       if (res == qman_cb_dqrr_stop)
+                               break;
+                       /* Check for VDQCR completion */
+                       if (dq->stat & QM_DQRR_STAT_DQCR_EXPIRED)
+                               clear_vdqcr(p, fq);
+               } else {
+                       /* SDQCR: contextB points to the FQ */
+                       fq = tag_to_fq(dq->contextB);
+                       /* Now let the callback do its stuff */
+                       res = fq->cb.dqrr(p, fq, dq);
+                       /*
+                        * The callback can request that we exit without
+                        * consuming this entry nor advancing;
+                        */
+                       if (res == qman_cb_dqrr_stop)
+                               break;
+               }
+               /* Interpret 'dq' from a driver perspective. */
+               /*
+                * Parking isn't possible unless HELDACTIVE was set. NB,
+                * FORCEELIGIBLE implies HELDACTIVE, so we only need to
+                * check for HELDACTIVE to cover both.
+                */
+               DPAA_ASSERT((dq->stat & QM_DQRR_STAT_FQ_HELDACTIVE) ||
+                           (res != qman_cb_dqrr_park));
+               /* just means "skip it, I'll consume it myself later on" */
+               if (res != qman_cb_dqrr_defer)
+                       qm_dqrr_cdc_consume_1ptr(&p->p, dq,
+                                                res == qman_cb_dqrr_park);
+               /* Move forward */
+               qm_dqrr_next(&p->p);
+               /*
+                * Entry processed and consumed, increment our counter.  The
+                * callback can request that we exit after consuming the
+                * entry, and we also exit if we reach our processing limit,
+                * so loop back only if neither of these conditions is met.
+                */
+       } while (++limit < poll_limit && res != qman_cb_dqrr_consume_stop);
+
+       return limit;
+}
+
+void qman_p_irqsource_add(struct qman_portal *p, u32 bits)
+{
+       unsigned long irqflags;
+
+       local_irq_save(irqflags);
+       set_bits(bits & QM_PIRQ_VISIBLE, &p->irq_sources);
+       qm_out(&p->p, QM_REG_IER, p->irq_sources);
+       local_irq_restore(irqflags);
+}
+EXPORT_SYMBOL(qman_p_irqsource_add);
+
+void qman_p_irqsource_remove(struct qman_portal *p, u32 bits)
+{
+       unsigned long irqflags;
+       u32 ier;
+
+       /*
+        * Our interrupt handler only processes+clears status register bits that
+        * are in p->irq_sources. As we're trimming that mask, if one of them
+        * were to assert in the status register just before we remove it from
+        * the enable register, there would be an interrupt-storm when we
+        * release the IRQ lock. So we wait for the enable register update to
+        * take effect in h/w (by reading it back) and then clear all other bits
+        * in the status register. Ie. we clear them from ISR once it's certain
+        * IER won't allow them to reassert.
+        */
+       local_irq_save(irqflags);
+       bits &= QM_PIRQ_VISIBLE;
+       clear_bits(bits, &p->irq_sources);
+       qm_out(&p->p, QM_REG_IER, p->irq_sources);
+       ier = qm_in(&p->p, QM_REG_IER);
+       /*
+        * Using "~ier" (rather than "bits" or "~p->irq_sources") creates a
+        * data-dependency, ie. to protect against re-ordering.
+        */
+       qm_out(&p->p, QM_REG_ISR, ~ier);
+       local_irq_restore(irqflags);
+}
+EXPORT_SYMBOL(qman_p_irqsource_remove);
+
+const cpumask_t *qman_affine_cpus(void)
+{
+       return &affine_mask;
+}
+EXPORT_SYMBOL(qman_affine_cpus);
+
+u16 qman_affine_channel(int cpu)
+{
+       if (cpu < 0) {
+               struct qman_portal *portal = get_affine_portal();
+
+               cpu = portal->config->cpu;
+               put_affine_portal();
+       }
+       WARN_ON(!cpumask_test_cpu(cpu, &affine_mask));
+       return affine_channels[cpu];
+}
+EXPORT_SYMBOL(qman_affine_channel);
+
+struct qman_portal *qman_get_affine_portal(int cpu)
+{
+       return affine_portals[cpu];
+}
+EXPORT_SYMBOL(qman_get_affine_portal);
+
+int qman_p_poll_dqrr(struct qman_portal *p, unsigned int limit)
+{
+       return __poll_portal_fast(p, limit);
+}
+EXPORT_SYMBOL(qman_p_poll_dqrr);
+
+void qman_p_static_dequeue_add(struct qman_portal *p, u32 pools)
+{
+       unsigned long irqflags;
+
+       local_irq_save(irqflags);
+       pools &= p->config->pools;
+       p->sdqcr |= pools;
+       qm_dqrr_sdqcr_set(&p->p, p->sdqcr);
+       local_irq_restore(irqflags);
+}
+EXPORT_SYMBOL(qman_p_static_dequeue_add);
+
+/* Frame queue API */
+
+static const char *mcr_result_str(u8 result)
+{
+       switch (result) {
+       case QM_MCR_RESULT_NULL:
+               return "QM_MCR_RESULT_NULL";
+       case QM_MCR_RESULT_OK:
+               return "QM_MCR_RESULT_OK";
+       case QM_MCR_RESULT_ERR_FQID:
+               return "QM_MCR_RESULT_ERR_FQID";
+       case QM_MCR_RESULT_ERR_FQSTATE:
+               return "QM_MCR_RESULT_ERR_FQSTATE";
+       case QM_MCR_RESULT_ERR_NOTEMPTY:
+               return "QM_MCR_RESULT_ERR_NOTEMPTY";
+       case QM_MCR_RESULT_PENDING:
+               return "QM_MCR_RESULT_PENDING";
+       case QM_MCR_RESULT_ERR_BADCOMMAND:
+               return "QM_MCR_RESULT_ERR_BADCOMMAND";
+       }
+       return "<unknown MCR result>";
+}
+
+int qman_create_fq(u32 fqid, u32 flags, struct qman_fq *fq)
+{
+       if (flags & QMAN_FQ_FLAG_DYNAMIC_FQID) {
+               int ret = qman_alloc_fqid(&fqid);
+
+               if (ret)
+                       return ret;
+       }
+       fq->fqid = fqid;
+       fq->flags = flags;
+       fq->state = qman_fq_state_oos;
+       fq->cgr_groupid = 0;
+
+       /* A context_b of 0 is allegedly special, so don't use that fqid */
+       if (fqid == 0 || fqid >= num_fqids) {
+               WARN(1, "bad fqid %d\n", fqid);
+               return -EINVAL;
+       }
+
+       fq->idx = fqid * 2;
+       if (flags & QMAN_FQ_FLAG_NO_MODIFY)
+               fq->idx++;
+
+       WARN_ON(fq_table[fq->idx]);
+       fq_table[fq->idx] = fq;
+
+       return 0;
+}
+EXPORT_SYMBOL(qman_create_fq);
+
+void qman_destroy_fq(struct qman_fq *fq)
+{
+       /*
+        * We don't need to lock the FQ as it is a pre-condition that the FQ be
+        * quiesced. Instead, run some checks.
+        */
+       switch (fq->state) {
+       case qman_fq_state_parked:
+       case qman_fq_state_oos:
+               if (fq_isset(fq, QMAN_FQ_FLAG_DYNAMIC_FQID))
+                       qman_release_fqid(fq->fqid);
+
+               DPAA_ASSERT(fq_table[fq->idx]);
+               fq_table[fq->idx] = NULL;
+               return;
+       default:
+               break;
+       }
+       DPAA_ASSERT(NULL == "qman_free_fq() on unquiesced FQ!");
+}
+EXPORT_SYMBOL(qman_destroy_fq);
+
+u32 qman_fq_fqid(struct qman_fq *fq)
+{
+       return fq->fqid;
+}
+EXPORT_SYMBOL(qman_fq_fqid);
+
+int qman_init_fq(struct qman_fq *fq, u32 flags, struct qm_mcc_initfq *opts)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p;
+       u8 res, myverb;
+       int ret = 0;
+
+       myverb = (flags & QMAN_INITFQ_FLAG_SCHED)
+               ? QM_MCC_VERB_INITFQ_SCHED : QM_MCC_VERB_INITFQ_PARKED;
+
+       if (fq->state != qman_fq_state_oos &&
+           fq->state != qman_fq_state_parked)
+               return -EINVAL;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       if (fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))
+               return -EINVAL;
+#endif
+       if (opts && (opts->we_mask & QM_INITFQ_WE_OAC)) {
+               /* And can't be set at the same time as TDTHRESH */
+               if (opts->we_mask & QM_INITFQ_WE_TDTHRESH)
+                       return -EINVAL;
+       }
+       /* Issue an INITFQ_[PARKED|SCHED] management command */
+       p = get_affine_portal();
+       if (fq_isset(fq, QMAN_FQ_STATE_CHANGING) ||
+           (fq->state != qman_fq_state_oos &&
+            fq->state != qman_fq_state_parked)) {
+               ret = -EBUSY;
+               goto out;
+       }
+       mcc = qm_mc_start(&p->p);
+       if (opts)
+               mcc->initfq = *opts;
+       mcc->initfq.fqid = fq->fqid;
+       mcc->initfq.count = 0;
+       /*
+        * If the FQ does *not* have the TO_DCPORTAL flag, contextB is set as a
+        * demux pointer. Otherwise, the caller-provided value is allowed to
+        * stand, don't overwrite it.
+        */
+       if (fq_isclear(fq, QMAN_FQ_FLAG_TO_DCPORTAL)) {
+               dma_addr_t phys_fq;
+
+               mcc->initfq.we_mask |= QM_INITFQ_WE_CONTEXTB;
+               mcc->initfq.fqd.context_b = fq_to_tag(fq);
+               /*
+                *  and the physical address - NB, if the user wasn't trying to
+                * set CONTEXTA, clear the stashing settings.
+                */
+               if (!(mcc->initfq.we_mask & QM_INITFQ_WE_CONTEXTA)) {
+                       mcc->initfq.we_mask |= QM_INITFQ_WE_CONTEXTA;
+                       memset(&mcc->initfq.fqd.context_a, 0,
+                               sizeof(mcc->initfq.fqd.context_a));
+               } else {
+                       phys_fq = dma_map_single(&p->pdev->dev, fq, sizeof(*fq),
+                                                DMA_TO_DEVICE);
+                       qm_fqd_stashing_set64(&mcc->initfq.fqd, phys_fq);
+               }
+       }
+       if (flags & QMAN_INITFQ_FLAG_LOCAL) {
+               int wq = 0;
+
+               if (!(mcc->initfq.we_mask & QM_INITFQ_WE_DESTWQ)) {
+                       mcc->initfq.we_mask |= QM_INITFQ_WE_DESTWQ;
+                       wq = 4;
+               }
+               qm_fqd_set_destwq(&mcc->initfq.fqd, p->config->channel, wq);
+       }
+       qm_mc_commit(&p->p, myverb);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               dev_err(p->config->dev, "MCR timeout\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == myverb);
+       res = mcr->result;
+       if (res != QM_MCR_RESULT_OK) {
+               ret = -EIO;
+               goto out;
+       }
+       if (opts) {
+               if (opts->we_mask & QM_INITFQ_WE_FQCTRL) {
+                       if (opts->fqd.fq_ctrl & QM_FQCTRL_CGE)
+                               fq_set(fq, QMAN_FQ_STATE_CGR_EN);
+                       else
+                               fq_clear(fq, QMAN_FQ_STATE_CGR_EN);
+               }
+               if (opts->we_mask & QM_INITFQ_WE_CGID)
+                       fq->cgr_groupid = opts->fqd.cgid;
+       }
+       fq->state = (flags & QMAN_INITFQ_FLAG_SCHED) ?
+               qman_fq_state_sched : qman_fq_state_parked;
+
+out:
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_init_fq);
+
+int qman_schedule_fq(struct qman_fq *fq)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p;
+       int ret = 0;
+
+       if (fq->state != qman_fq_state_parked)
+               return -EINVAL;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       if (fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))
+               return -EINVAL;
+#endif
+       /* Issue a ALTERFQ_SCHED management command */
+       p = get_affine_portal();
+       if (fq_isset(fq, QMAN_FQ_STATE_CHANGING) ||
+           fq->state != qman_fq_state_parked) {
+               ret = -EBUSY;
+               goto out;
+       }
+       mcc = qm_mc_start(&p->p);
+       mcc->alterfq.fqid = fq->fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_SCHED);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               dev_err(p->config->dev, "ALTER_SCHED timeout\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_SCHED);
+       if (mcr->result != QM_MCR_RESULT_OK) {
+               ret = -EIO;
+               goto out;
+       }
+       fq->state = qman_fq_state_sched;
+out:
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_schedule_fq);
+
+int qman_retire_fq(struct qman_fq *fq, u32 *flags)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p;
+       int ret;
+       u8 res;
+
+       if (fq->state != qman_fq_state_parked &&
+           fq->state != qman_fq_state_sched)
+               return -EINVAL;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       if (fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))
+               return -EINVAL;
+#endif
+       p = get_affine_portal();
+       if (fq_isset(fq, QMAN_FQ_STATE_CHANGING) ||
+           fq->state == qman_fq_state_retired ||
+           fq->state == qman_fq_state_oos) {
+               ret = -EBUSY;
+               goto out;
+       }
+       mcc = qm_mc_start(&p->p);
+       mcc->alterfq.fqid = fq->fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               dev_crit(p->config->dev, "ALTER_RETIRE timeout\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_RETIRE);
+       res = mcr->result;
+       /*
+        * "Elegant" would be to treat OK/PENDING the same way; set CHANGING,
+        * and defer the flags until FQRNI or FQRN (respectively) show up. But
+        * "Friendly" is to process OK immediately, and not set CHANGING. We do
+        * friendly, otherwise the caller doesn't necessarily have a fully
+        * "retired" FQ on return even if the retirement was immediate. However
+        * this does mean some code duplication between here and
+        * fq_state_change().
+        */
+       if (res == QM_MCR_RESULT_OK) {
+               ret = 0;
+               /* Process 'fq' right away, we'll ignore FQRNI */
+               if (mcr->alterfq.fqs & QM_MCR_FQS_NOTEMPTY)
+                       fq_set(fq, QMAN_FQ_STATE_NE);
+               if (mcr->alterfq.fqs & QM_MCR_FQS_ORLPRESENT)
+                       fq_set(fq, QMAN_FQ_STATE_ORL);
+               if (flags)
+                       *flags = fq->flags;
+               fq->state = qman_fq_state_retired;
+               if (fq->cb.fqs) {
+                       /*
+                        * Another issue with supporting "immediate" retirement
+                        * is that we're forced to drop FQRNIs, because by the
+                        * time they're seen it may already be "too late" (the
+                        * fq may have been OOS'd and free()'d already). But if
+                        * the upper layer wants a callback whether it's
+                        * immediate or not, we have to fake a "MR" entry to
+                        * look like an FQRNI...
+                        */
+                       union qm_mr_entry msg;
+
+                       msg.verb = QM_MR_VERB_FQRNI;
+                       msg.fq.fqs = mcr->alterfq.fqs;
+                       msg.fq.fqid = fq->fqid;
+                       msg.fq.contextB = fq_to_tag(fq);
+                       fq->cb.fqs(p, fq, &msg);
+               }
+       } else if (res == QM_MCR_RESULT_PENDING) {
+               ret = 1;
+               fq_set(fq, QMAN_FQ_STATE_CHANGING);
+       } else {
+               ret = -EIO;
+       }
+out:
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_retire_fq);
+
+int qman_oos_fq(struct qman_fq *fq)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p;
+       int ret = 0;
+
+       if (fq->state != qman_fq_state_retired)
+               return -EINVAL;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       if (fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))
+               return -EINVAL;
+#endif
+       p = get_affine_portal();
+       if (fq_isset(fq, QMAN_FQ_STATE_BLOCKOOS) ||
+           fq->state != qman_fq_state_retired) {
+               ret = -EBUSY;
+               goto out;
+       }
+       mcc = qm_mc_start(&p->p);
+       mcc->alterfq.fqid = fq->fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_OOS);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_OOS);
+       if (mcr->result != QM_MCR_RESULT_OK) {
+               ret = -EIO;
+               goto out;
+       }
+       fq->state = qman_fq_state_oos;
+out:
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_oos_fq);
+
+int qman_query_fq(struct qman_fq *fq, struct qm_fqd *fqd)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p = get_affine_portal();
+       int ret = 0;
+
+       mcc = qm_mc_start(&p->p);
+       mcc->queryfq.fqid = fq->fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ);
+       if (mcr->result == QM_MCR_RESULT_OK)
+               *fqd = mcr->queryfq.fqd;
+       else
+               ret = -EIO;
+out:
+       put_affine_portal();
+       return ret;
+}
+
+static int qman_query_fq_np(struct qman_fq *fq,
+                           struct qm_mcr_queryfq_np *np)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p = get_affine_portal();
+       int ret = 0;
+
+       mcc = qm_mc_start(&p->p);
+       mcc->queryfq.fqid = fq->fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ_NP);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ_NP);
+       if (mcr->result == QM_MCR_RESULT_OK)
+               *np = mcr->queryfq_np;
+       else if (mcr->result == QM_MCR_RESULT_ERR_FQID)
+               ret = -ERANGE;
+       else
+               ret = -EIO;
+out:
+       put_affine_portal();
+       return ret;
+}
+
+static int qman_query_cgr(struct qman_cgr *cgr,
+                         struct qm_mcr_querycgr *cgrd)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p = get_affine_portal();
+       int ret = 0;
+
+       mcc = qm_mc_start(&p->p);
+       mcc->querycgr.cgid = cgr->cgrid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCGR);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCC_VERB_QUERYCGR);
+       if (mcr->result == QM_MCR_RESULT_OK)
+               *cgrd = mcr->querycgr;
+       else {
+               dev_err(p->config->dev, "QUERY_CGR failed: %s\n",
+                       mcr_result_str(mcr->result));
+               ret = -EIO;
+       }
+out:
+       put_affine_portal();
+       return ret;
+}
+
+int qman_query_cgr_congested(struct qman_cgr *cgr, bool *result)
+{
+       struct qm_mcr_querycgr query_cgr;
+       int err;
+
+       err = qman_query_cgr(cgr, &query_cgr);
+       if (err)
+               return err;
+
+       *result = !!query_cgr.cgr.cs;
+       return 0;
+}
+EXPORT_SYMBOL(qman_query_cgr_congested);
+
+/* internal function used as a wait_event() expression */
+static int set_p_vdqcr(struct qman_portal *p, struct qman_fq *fq, u32 vdqcr)
+{
+       unsigned long irqflags;
+       int ret = -EBUSY;
+
+       local_irq_save(irqflags);
+       if (p->vdqcr_owned)
+               goto out;
+       if (fq_isset(fq, QMAN_FQ_STATE_VDQCR))
+               goto out;
+
+       fq_set(fq, QMAN_FQ_STATE_VDQCR);
+       p->vdqcr_owned = fq;
+       qm_dqrr_vdqcr_set(&p->p, vdqcr);
+       ret = 0;
+out:
+       local_irq_restore(irqflags);
+       return ret;
+}
+
+static int set_vdqcr(struct qman_portal **p, struct qman_fq *fq, u32 vdqcr)
+{
+       int ret;
+
+       *p = get_affine_portal();
+       ret = set_p_vdqcr(*p, fq, vdqcr);
+       put_affine_portal();
+       return ret;
+}
+
+static int wait_vdqcr_start(struct qman_portal **p, struct qman_fq *fq,
+                               u32 vdqcr, u32 flags)
+{
+       int ret = 0;
+
+       if (flags & QMAN_VOLATILE_FLAG_WAIT_INT)
+               ret = wait_event_interruptible(affine_queue,
+                               !set_vdqcr(p, fq, vdqcr));
+       else
+               wait_event(affine_queue, !set_vdqcr(p, fq, vdqcr));
+       return ret;
+}
+
+int qman_volatile_dequeue(struct qman_fq *fq, u32 flags, u32 vdqcr)
+{
+       struct qman_portal *p;
+       int ret;
+
+       if (fq->state != qman_fq_state_parked &&
+           fq->state != qman_fq_state_retired)
+               return -EINVAL;
+       if (vdqcr & QM_VDQCR_FQID_MASK)
+               return -EINVAL;
+       if (fq_isset(fq, QMAN_FQ_STATE_VDQCR))
+               return -EBUSY;
+       vdqcr = (vdqcr & ~QM_VDQCR_FQID_MASK) | fq->fqid;
+       if (flags & QMAN_VOLATILE_FLAG_WAIT)
+               ret = wait_vdqcr_start(&p, fq, vdqcr, flags);
+       else
+               ret = set_vdqcr(&p, fq, vdqcr);
+       if (ret)
+               return ret;
+       /* VDQCR is set */
+       if (flags & QMAN_VOLATILE_FLAG_FINISH) {
+               if (flags & QMAN_VOLATILE_FLAG_WAIT_INT)
+                       /*
+                        * NB: don't propagate any error - the caller wouldn't
+                        * know whether the VDQCR was issued or not. A signal
+                        * could arrive after returning anyway, so the caller
+                        * can check signal_pending() if that's an issue.
+                        */
+                       wait_event_interruptible(affine_queue,
+                               !fq_isset(fq, QMAN_FQ_STATE_VDQCR));
+               else
+                       wait_event(affine_queue,
+                               !fq_isset(fq, QMAN_FQ_STATE_VDQCR));
+       }
+       return 0;
+}
+EXPORT_SYMBOL(qman_volatile_dequeue);
+
+static void update_eqcr_ci(struct qman_portal *p, u8 avail)
+{
+       if (avail)
+               qm_eqcr_cce_prefetch(&p->p);
+       else
+               qm_eqcr_cce_update(&p->p);
+}
+
+int qman_enqueue(struct qman_fq *fq, const struct qm_fd *fd)
+{
+       struct qman_portal *p;
+       struct qm_eqcr_entry *eq;
+       unsigned long irqflags;
+       u8 avail;
+
+       p = get_affine_portal();
+       local_irq_save(irqflags);
+
+       if (p->use_eqcr_ci_stashing) {
+               /*
+                * The stashing case is easy, only update if we need to in
+                * order to try and liberate ring entries.
+                */
+               eq = qm_eqcr_start_stash(&p->p);
+       } else {
+               /*
+                * The non-stashing case is harder, need to prefetch ahead of
+                * time.
+                */
+               avail = qm_eqcr_get_avail(&p->p);
+               if (avail < 2)
+                       update_eqcr_ci(p, avail);
+               eq = qm_eqcr_start_no_stash(&p->p);
+       }
+
+       if (unlikely(!eq))
+               goto out;
+
+       eq->fqid = fq->fqid;
+       eq->tag = fq_to_tag(fq);
+       eq->fd = *fd;
+
+       qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_CMD_ENQUEUE);
+out:
+       local_irq_restore(irqflags);
+       put_affine_portal();
+       return 0;
+}
+EXPORT_SYMBOL(qman_enqueue);
+
+static int qm_modify_cgr(struct qman_cgr *cgr, u32 flags,
+                        struct qm_mcc_initcgr *opts)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p = get_affine_portal();
+       u8 verb = QM_MCC_VERB_MODIFYCGR;
+       int ret = 0;
+
+       mcc = qm_mc_start(&p->p);
+       if (opts)
+               mcc->initcgr = *opts;
+       mcc->initcgr.cgid = cgr->cgrid;
+       if (flags & QMAN_CGR_FLAG_USE_INIT)
+               verb = QM_MCC_VERB_INITCGR;
+       qm_mc_commit(&p->p, verb);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == verb);
+       if (mcr->result != QM_MCR_RESULT_OK)
+               ret = -EIO;
+
+out:
+       put_affine_portal();
+       return ret;
+}
+
+#define PORTAL_IDX(n)  (n->config->channel - QM_CHANNEL_SWPORTAL0)
+#define TARG_MASK(n)   (BIT(31) >> PORTAL_IDX(n))
+
+static u8 qman_cgr_cpus[CGR_NUM];
+
+void qman_init_cgr_all(void)
+{
+       struct qman_cgr cgr;
+       int err_cnt = 0;
+
+       for (cgr.cgrid = 0; cgr.cgrid < CGR_NUM; cgr.cgrid++) {
+               if (qm_modify_cgr(&cgr, QMAN_CGR_FLAG_USE_INIT, NULL))
+                       err_cnt++;
+       }
+
+       if (err_cnt)
+               pr_err("Warning: %d error%s while initialising CGR h/w\n",
+                      err_cnt, (err_cnt > 1) ? "s" : "");
+}
+
+int qman_create_cgr(struct qman_cgr *cgr, u32 flags,
+                   struct qm_mcc_initcgr *opts)
+{
+       struct qm_mcr_querycgr cgr_state;
+       struct qm_mcc_initcgr local_opts = {};
+       int ret;
+       struct qman_portal *p;
+
+       /*
+        * We have to check that the provided CGRID is within the limits of the
+        * data-structures, for obvious reasons. However we'll let h/w take
+        * care of determining whether it's within the limits of what exists on
+        * the SoC.
+        */
+       if (cgr->cgrid >= CGR_NUM)
+               return -EINVAL;
+
+       preempt_disable();
+       p = get_affine_portal();
+       qman_cgr_cpus[cgr->cgrid] = smp_processor_id();
+       preempt_enable();
+
+       cgr->chan = p->config->channel;
+       spin_lock(&p->cgr_lock);
+
+       if (opts) {
+               ret = qman_query_cgr(cgr, &cgr_state);
+               if (ret)
+                       goto out;
+               if (opts)
+                       local_opts = *opts;
+               if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
+                       local_opts.cgr.cscn_targ_upd_ctrl =
+                               QM_CGR_TARG_UDP_CTRL_WRITE_BIT | PORTAL_IDX(p);
+               else
+                       /* Overwrite TARG */
+                       local_opts.cgr.cscn_targ = cgr_state.cgr.cscn_targ |
+                                                  TARG_MASK(p);
+               local_opts.we_mask |= QM_CGR_WE_CSCN_TARG;
+
+               /* send init if flags indicate so */
+               if (opts && (flags & QMAN_CGR_FLAG_USE_INIT))
+                       ret = qm_modify_cgr(cgr, QMAN_CGR_FLAG_USE_INIT,
+                                           &local_opts);
+               else
+                       ret = qm_modify_cgr(cgr, 0, &local_opts);
+               if (ret)
+                       goto out;
+       }
+
+       list_add(&cgr->node, &p->cgr_cbs);
+
+       /* Determine if newly added object requires its callback to be called */
+       ret = qman_query_cgr(cgr, &cgr_state);
+       if (ret) {
+               /* we can't go back, so proceed and return success */
+               dev_err(p->config->dev, "CGR HW state partially modified\n");
+               ret = 0;
+               goto out;
+       }
+       if (cgr->cb && cgr_state.cgr.cscn_en &&
+           qman_cgrs_get(&p->cgrs[1], cgr->cgrid))
+               cgr->cb(p, cgr, 1);
+out:
+       spin_unlock(&p->cgr_lock);
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_create_cgr);
+
+int qman_delete_cgr(struct qman_cgr *cgr)
+{
+       unsigned long irqflags;
+       struct qm_mcr_querycgr cgr_state;
+       struct qm_mcc_initcgr local_opts;
+       int ret = 0;
+       struct qman_cgr *i;
+       struct qman_portal *p = get_affine_portal();
+
+       if (cgr->chan != p->config->channel) {
+               /* attempt to delete from other portal than creator */
+               dev_err(p->config->dev, "CGR not owned by current portal");
+               dev_dbg(p->config->dev, " create 0x%x, delete 0x%x\n",
+                       cgr->chan, p->config->channel);
+
+               ret = -EINVAL;
+               goto put_portal;
+       }
+       memset(&local_opts, 0, sizeof(struct qm_mcc_initcgr));
+       spin_lock_irqsave(&p->cgr_lock, irqflags);
+       list_del(&cgr->node);
+       /*
+        * If there are no other CGR objects for this CGRID in the list,
+        * update CSCN_TARG accordingly
+        */
+       list_for_each_entry(i, &p->cgr_cbs, node)
+               if (i->cgrid == cgr->cgrid && i->cb)
+                       goto release_lock;
+       ret = qman_query_cgr(cgr, &cgr_state);
+       if (ret)  {
+               /* add back to the list */
+               list_add(&cgr->node, &p->cgr_cbs);
+               goto release_lock;
+       }
+       /* Overwrite TARG */
+       local_opts.we_mask = QM_CGR_WE_CSCN_TARG;
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
+               local_opts.cgr.cscn_targ_upd_ctrl = PORTAL_IDX(p);
+       else
+               local_opts.cgr.cscn_targ = cgr_state.cgr.cscn_targ &
+                                                        ~(TARG_MASK(p));
+       ret = qm_modify_cgr(cgr, 0, &local_opts);
+       if (ret)
+               /* add back to the list */
+               list_add(&cgr->node, &p->cgr_cbs);
+release_lock:
+       spin_unlock_irqrestore(&p->cgr_lock, irqflags);
+put_portal:
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_delete_cgr);
+
+struct cgr_comp {
+       struct qman_cgr *cgr;
+       struct completion completion;
+};
+
+static int qman_delete_cgr_thread(void *p)
+{
+       struct cgr_comp *cgr_comp = (struct cgr_comp *)p;
+       int ret;
+
+       ret = qman_delete_cgr(cgr_comp->cgr);
+       complete(&cgr_comp->completion);
+
+       return ret;
+}
+
+void qman_delete_cgr_safe(struct qman_cgr *cgr)
+{
+       struct task_struct *thread;
+       struct cgr_comp cgr_comp;
+
+       preempt_disable();
+       if (qman_cgr_cpus[cgr->cgrid] != smp_processor_id()) {
+               init_completion(&cgr_comp.completion);
+               cgr_comp.cgr = cgr;
+               thread = kthread_create(qman_delete_cgr_thread, &cgr_comp,
+                                       "cgr_del");
+
+               if (IS_ERR(thread))
+                       goto out;
+
+               kthread_bind(thread, qman_cgr_cpus[cgr->cgrid]);
+               wake_up_process(thread);
+               wait_for_completion(&cgr_comp.completion);
+               preempt_enable();
+               return;
+       }
+out:
+       qman_delete_cgr(cgr);
+       preempt_enable();
+}
+EXPORT_SYMBOL(qman_delete_cgr_safe);
+
+/* Cleanup FQs */
+
+static int _qm_mr_consume_and_match_verb(struct qm_portal *p, int v)
+{
+       const union qm_mr_entry *msg;
+       int found = 0;
+
+       qm_mr_pvb_update(p);
+       msg = qm_mr_current(p);
+       while (msg) {
+               if ((msg->verb & QM_MR_VERB_TYPE_MASK) == v)
+                       found = 1;
+               qm_mr_next(p);
+               qm_mr_cci_consume_to_current(p);
+               qm_mr_pvb_update(p);
+               msg = qm_mr_current(p);
+       }
+       return found;
+}
+
+static int _qm_dqrr_consume_and_match(struct qm_portal *p, u32 fqid, int s,
+                                     bool wait)
+{
+       const struct qm_dqrr_entry *dqrr;
+       int found = 0;
+
+       do {
+               qm_dqrr_pvb_update(p);
+               dqrr = qm_dqrr_current(p);
+               if (!dqrr)
+                       cpu_relax();
+       } while (wait && !dqrr);
+
+       while (dqrr) {
+               if (dqrr->fqid == fqid && (dqrr->stat & s))
+                       found = 1;
+               qm_dqrr_cdc_consume_1ptr(p, dqrr, 0);
+               qm_dqrr_pvb_update(p);
+               qm_dqrr_next(p);
+               dqrr = qm_dqrr_current(p);
+       }
+       return found;
+}
+
+#define qm_mr_drain(p, V) \
+       _qm_mr_consume_and_match_verb(p, QM_MR_VERB_##V)
+
+#define qm_dqrr_drain(p, f, S) \
+       _qm_dqrr_consume_and_match(p, f, QM_DQRR_STAT_##S, false)
+
+#define qm_dqrr_drain_wait(p, f, S) \
+       _qm_dqrr_consume_and_match(p, f, QM_DQRR_STAT_##S, true)
+
+#define qm_dqrr_drain_nomatch(p) \
+       _qm_dqrr_consume_and_match(p, 0, 0, false)
+
+static int qman_shutdown_fq(u32 fqid)
+{
+       struct qman_portal *p;
+       struct device *dev;
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       int orl_empty, drain = 0, ret = 0;
+       u32 channel, wq, res;
+       u8 state;
+
+       p = get_affine_portal();
+       dev = p->config->dev;
+       /* Determine the state of the FQID */
+       mcc = qm_mc_start(&p->p);
+       mcc->queryfq_np.fqid = fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ_NP);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               dev_err(dev, "QUERYFQ_NP timeout\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ_NP);
+       state = mcr->queryfq_np.state & QM_MCR_NP_STATE_MASK;
+       if (state == QM_MCR_NP_STATE_OOS)
+               goto out; /* Already OOS, no need to do anymore checks */
+
+       /* Query which channel the FQ is using */
+       mcc = qm_mc_start(&p->p);
+       mcc->queryfq.fqid = fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               dev_err(dev, "QUERYFQ timeout\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ);
+       /* Need to store these since the MCR gets reused */
+       channel = qm_fqd_get_chan(&mcr->queryfq.fqd);
+       wq = qm_fqd_get_wq(&mcr->queryfq.fqd);
+
+       switch (state) {
+       case QM_MCR_NP_STATE_TEN_SCHED:
+       case QM_MCR_NP_STATE_TRU_SCHED:
+       case QM_MCR_NP_STATE_ACTIVE:
+       case QM_MCR_NP_STATE_PARKED:
+               orl_empty = 0;
+               mcc = qm_mc_start(&p->p);
+               mcc->alterfq.fqid = fqid;
+               qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE);
+               if (!qm_mc_result_timeout(&p->p, &mcr)) {
+                       dev_err(dev, "QUERYFQ_NP timeout\n");
+                       ret = -ETIMEDOUT;
+                       goto out;
+               }
+               DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
+                           QM_MCR_VERB_ALTER_RETIRE);
+               res = mcr->result; /* Make a copy as we reuse MCR below */
+
+               if (res == QM_MCR_RESULT_PENDING) {
+                       /*
+                        * Need to wait for the FQRN in the message ring, which
+                        * will only occur once the FQ has been drained.  In
+                        * order for the FQ to drain the portal needs to be set
+                        * to dequeue from the channel the FQ is scheduled on
+                        */
+                       int found_fqrn = 0;
+                       u16 dequeue_wq = 0;
+
+                       /* Flag that we need to drain FQ */
+                       drain = 1;
+
+                       if (channel >= qm_channel_pool1 &&
+                           channel < qm_channel_pool1 + 15) {
+                               /* Pool channel, enable the bit in the portal */
+                               dequeue_wq = (channel -
+                                             qm_channel_pool1 + 1)<<4 | wq;
+                       } else if (channel < qm_channel_pool1) {
+                               /* Dedicated channel */
+                               dequeue_wq = wq;
+                       } else {
+                               dev_err(dev, "Can't recover FQ 0x%x, ch: 0x%x",
+                                       fqid, channel);
+                               ret = -EBUSY;
+                               goto out;
+                       }
+                       /* Set the sdqcr to drain this channel */
+                       if (channel < qm_channel_pool1)
+                               qm_dqrr_sdqcr_set(&p->p,
+                                                 QM_SDQCR_TYPE_ACTIVE |
+                                                 QM_SDQCR_CHANNELS_DEDICATED);
+                       else
+                               qm_dqrr_sdqcr_set(&p->p,
+                                                 QM_SDQCR_TYPE_ACTIVE |
+                                                 QM_SDQCR_CHANNELS_POOL_CONV
+                                                 (channel));
+                       do {
+                               /* Keep draining DQRR while checking the MR*/
+                               qm_dqrr_drain_nomatch(&p->p);
+                               /* Process message ring too */
+                               found_fqrn = qm_mr_drain(&p->p, FQRN);
+                               cpu_relax();
+                       } while (!found_fqrn);
+
+               }
+               if (res != QM_MCR_RESULT_OK &&
+                   res != QM_MCR_RESULT_PENDING) {
+                       dev_err(dev, "retire_fq failed: FQ 0x%x, res=0x%x\n",
+                               fqid, res);
+                       ret = -EIO;
+                       goto out;
+               }
+               if (!(mcr->alterfq.fqs & QM_MCR_FQS_ORLPRESENT)) {
+                       /*
+                        * ORL had no entries, no need to wait until the
+                        * ERNs come in
+                        */
+                       orl_empty = 1;
+               }
+               /*
+                * Retirement succeeded, check to see if FQ needs
+                * to be drained
+                */
+               if (drain || mcr->alterfq.fqs & QM_MCR_FQS_NOTEMPTY) {
+                       /* FQ is Not Empty, drain using volatile DQ commands */
+                       do {
+                               u32 vdqcr = fqid | QM_VDQCR_NUMFRAMES_SET(3);
+
+                               qm_dqrr_vdqcr_set(&p->p, vdqcr);
+                               /*
+                                * Wait for a dequeue and process the dequeues,
+                                * making sure to empty the ring completely
+                                */
+                       } while (qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY));
+               }
+               qm_dqrr_sdqcr_set(&p->p, 0);
+
+               while (!orl_empty) {
+                       /* Wait for the ORL to have been completely drained */
+                       orl_empty = qm_mr_drain(&p->p, FQRL);
+                       cpu_relax();
+               }
+               mcc = qm_mc_start(&p->p);
+               mcc->alterfq.fqid = fqid;
+               qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_OOS);
+               if (!qm_mc_result_timeout(&p->p, &mcr)) {
+                       ret = -ETIMEDOUT;
+                       goto out;
+               }
+
+               DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
+                           QM_MCR_VERB_ALTER_OOS);
+               if (mcr->result != QM_MCR_RESULT_OK) {
+                       dev_err(dev, "OOS after drain fail: FQ 0x%x (0x%x)\n",
+                               fqid, mcr->result);
+                       ret = -EIO;
+                       goto out;
+               }
+               break;
+
+       case QM_MCR_NP_STATE_RETIRED:
+               /* Send OOS Command */
+               mcc = qm_mc_start(&p->p);
+               mcc->alterfq.fqid = fqid;
+               qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_OOS);
+               if (!qm_mc_result_timeout(&p->p, &mcr)) {
+                       ret = -ETIMEDOUT;
+                       goto out;
+               }
+
+               DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
+                           QM_MCR_VERB_ALTER_OOS);
+               if (mcr->result) {
+                       dev_err(dev, "OOS fail: FQ 0x%x (0x%x)\n",
+                               fqid, mcr->result);
+                       ret = -EIO;
+                       goto out;
+               }
+               break;
+
+       case QM_MCR_NP_STATE_OOS:
+               /*  Done */
+               break;
+
+       default:
+               ret = -EIO;
+       }
+
+out:
+       put_affine_portal();
+       return ret;
+}
+
+const struct qm_portal_config *qman_get_qm_portal_config(
+                                               struct qman_portal *portal)
+{
+       return portal->config;
+}
+
+struct gen_pool *qm_fqalloc; /* FQID allocator */
+struct gen_pool *qm_qpalloc; /* pool-channel allocator */
+struct gen_pool *qm_cgralloc; /* CGR ID allocator */
+
+static int qman_alloc_range(struct gen_pool *p, u32 *result, u32 cnt)
+{
+       unsigned long addr;
+
+       addr = gen_pool_alloc(p, cnt);
+       if (!addr)
+               return -ENOMEM;
+
+       *result = addr & ~DPAA_GENALLOC_OFF;
+
+       return 0;
+}
+
+int qman_alloc_fqid_range(u32 *result, u32 count)
+{
+       return qman_alloc_range(qm_fqalloc, result, count);
+}
+EXPORT_SYMBOL(qman_alloc_fqid_range);
+
+int qman_alloc_pool_range(u32 *result, u32 count)
+{
+       return qman_alloc_range(qm_qpalloc, result, count);
+}
+EXPORT_SYMBOL(qman_alloc_pool_range);
+
+int qman_alloc_cgrid_range(u32 *result, u32 count)
+{
+       return qman_alloc_range(qm_cgralloc, result, count);
+}
+EXPORT_SYMBOL(qman_alloc_cgrid_range);
+
+int qman_release_fqid(u32 fqid)
+{
+       int ret = qman_shutdown_fq(fqid);
+
+       if (ret) {
+               pr_debug("FQID %d leaked\n", fqid);
+               return ret;
+       }
+
+       gen_pool_free(qm_fqalloc, fqid | DPAA_GENALLOC_OFF, 1);
+       return 0;
+}
+EXPORT_SYMBOL(qman_release_fqid);
+
+static int qpool_cleanup(u32 qp)
+{
+       /*
+        * We query all FQDs starting from
+        * FQID 1 until we get an "invalid FQID" error, looking for non-OOS FQDs
+        * whose destination channel is the pool-channel being released.
+        * When a non-OOS FQD is found we attempt to clean it up
+        */
+       struct qman_fq fq = {
+               .fqid = QM_FQID_RANGE_START
+       };
+       int err;
+
+       do {
+               struct qm_mcr_queryfq_np np;
+
+               err = qman_query_fq_np(&fq, &np);
+               if (err)
+                       /* FQID range exceeded, found no problems */
+                       return 0;
+               if ((np.state & QM_MCR_NP_STATE_MASK) != QM_MCR_NP_STATE_OOS) {
+                       struct qm_fqd fqd;
+
+                       err = qman_query_fq(&fq, &fqd);
+                       if (WARN_ON(err))
+                               return 0;
+                       if (qm_fqd_get_chan(&fqd) == qp) {
+                               /* The channel is the FQ's target, clean it */
+                               err = qman_shutdown_fq(fq.fqid);
+                               if (err)
+                                       /*
+                                        * Couldn't shut down the FQ
+                                        * so the pool must be leaked
+                                        */
+                                       return err;
+                       }
+               }
+               /* Move to the next FQID */
+               fq.fqid++;
+       } while (1);
+}
+
+int qman_release_pool(u32 qp)
+{
+       int ret;
+
+       ret = qpool_cleanup(qp);
+       if (ret) {
+               pr_debug("CHID %d leaked\n", qp);
+               return ret;
+       }
+
+       gen_pool_free(qm_qpalloc, qp | DPAA_GENALLOC_OFF, 1);
+       return 0;
+}
+EXPORT_SYMBOL(qman_release_pool);
+
+static int cgr_cleanup(u32 cgrid)
+{
+       /*
+        * query all FQDs starting from FQID 1 until we get an "invalid FQID"
+        * error, looking for non-OOS FQDs whose CGR is the CGR being released
+        */
+       struct qman_fq fq = {
+               .fqid = 1
+       };
+       int err;
+
+       do {
+               struct qm_mcr_queryfq_np np;
+
+               err = qman_query_fq_np(&fq, &np);
+               if (err)
+                       /* FQID range exceeded, found no problems */
+                       return 0;
+               if ((np.state & QM_MCR_NP_STATE_MASK) != QM_MCR_NP_STATE_OOS) {
+                       struct qm_fqd fqd;
+
+                       err = qman_query_fq(&fq, &fqd);
+                       if (WARN_ON(err))
+                               return 0;
+                       if ((fqd.fq_ctrl & QM_FQCTRL_CGE) &&
+                           fqd.cgid == cgrid) {
+                               pr_err("CRGID 0x%x is being used by FQID 0x%x, CGR will be leaked\n",
+                                      cgrid, fq.fqid);
+                               return -EIO;
+                       }
+               }
+               /* Move to the next FQID */
+               fq.fqid++;
+       } while (1);
+}
+
+int qman_release_cgrid(u32 cgrid)
+{
+       int ret;
+
+       ret = cgr_cleanup(cgrid);
+       if (ret) {
+               pr_debug("CGRID %d leaked\n", cgrid);
+               return ret;
+       }
+
+       gen_pool_free(qm_cgralloc, cgrid | DPAA_GENALLOC_OFF, 1);
+       return 0;
+}
+EXPORT_SYMBOL(qman_release_cgrid);
diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c
new file mode 100644 (file)
index 0000000..0cace9e
--- /dev/null
@@ -0,0 +1,808 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_priv.h"
+
+u16 qman_ip_rev;
+EXPORT_SYMBOL(qman_ip_rev);
+u16 qm_channel_pool1 = QMAN_CHANNEL_POOL1;
+EXPORT_SYMBOL(qm_channel_pool1);
+
+/* Register offsets */
+#define REG_QCSP_LIO_CFG(n)    (0x0000 + ((n) * 0x10))
+#define REG_QCSP_IO_CFG(n)     (0x0004 + ((n) * 0x10))
+#define REG_QCSP_DD_CFG(n)     (0x000c + ((n) * 0x10))
+#define REG_DD_CFG             0x0200
+#define REG_DCP_CFG(n)         (0x0300 + ((n) * 0x10))
+#define REG_DCP_DD_CFG(n)      (0x0304 + ((n) * 0x10))
+#define REG_DCP_DLM_AVG(n)     (0x030c + ((n) * 0x10))
+#define REG_PFDR_FPC           0x0400
+#define REG_PFDR_FP_HEAD       0x0404
+#define REG_PFDR_FP_TAIL       0x0408
+#define REG_PFDR_FP_LWIT       0x0410
+#define REG_PFDR_CFG           0x0414
+#define REG_SFDR_CFG           0x0500
+#define REG_SFDR_IN_USE                0x0504
+#define REG_WQ_CS_CFG(n)       (0x0600 + ((n) * 0x04))
+#define REG_WQ_DEF_ENC_WQID    0x0630
+#define REG_WQ_SC_DD_CFG(n)    (0x640 + ((n) * 0x04))
+#define REG_WQ_PC_DD_CFG(n)    (0x680 + ((n) * 0x04))
+#define REG_WQ_DC0_DD_CFG(n)   (0x6c0 + ((n) * 0x04))
+#define REG_WQ_DC1_DD_CFG(n)   (0x700 + ((n) * 0x04))
+#define REG_WQ_DCn_DD_CFG(n)   (0x6c0 + ((n) * 0x40)) /* n=2,3 */
+#define REG_CM_CFG             0x0800
+#define REG_ECSR               0x0a00
+#define REG_ECIR               0x0a04
+#define REG_EADR               0x0a08
+#define REG_ECIR2              0x0a0c
+#define REG_EDATA(n)           (0x0a10 + ((n) * 0x04))
+#define REG_SBEC(n)            (0x0a80 + ((n) * 0x04))
+#define REG_MCR                        0x0b00
+#define REG_MCP(n)             (0x0b04 + ((n) * 0x04))
+#define REG_MISC_CFG           0x0be0
+#define REG_HID_CFG            0x0bf0
+#define REG_IDLE_STAT          0x0bf4
+#define REG_IP_REV_1           0x0bf8
+#define REG_IP_REV_2           0x0bfc
+#define REG_FQD_BARE           0x0c00
+#define REG_PFDR_BARE          0x0c20
+#define REG_offset_BAR         0x0004  /* relative to REG_[FQD|PFDR]_BARE */
+#define REG_offset_AR          0x0010  /* relative to REG_[FQD|PFDR]_BARE */
+#define REG_QCSP_BARE          0x0c80
+#define REG_QCSP_BAR           0x0c84
+#define REG_CI_SCHED_CFG       0x0d00
+#define REG_SRCIDR             0x0d04
+#define REG_LIODNR             0x0d08
+#define REG_CI_RLM_AVG         0x0d14
+#define REG_ERR_ISR            0x0e00
+#define REG_ERR_IER            0x0e04
+#define REG_REV3_QCSP_LIO_CFG(n)       (0x1000 + ((n) * 0x10))
+#define REG_REV3_QCSP_IO_CFG(n)        (0x1004 + ((n) * 0x10))
+#define REG_REV3_QCSP_DD_CFG(n)        (0x100c + ((n) * 0x10))
+
+/* Assists for QMAN_MCR */
+#define MCR_INIT_PFDR          0x01000000
+#define MCR_get_rslt(v)                (u8)((v) >> 24)
+#define MCR_rslt_idle(r)       (!(r) || ((r) >= 0xf0))
+#define MCR_rslt_ok(r)         ((r) == 0xf0)
+#define MCR_rslt_eaccess(r)    ((r) == 0xf8)
+#define MCR_rslt_inval(r)      ((r) == 0xff)
+
+/*
+ * Corenet initiator settings. Stash request queues are 4-deep to match cores
+ * ability to snarf. Stash priority is 3, other priorities are 2.
+ */
+#define QM_CI_SCHED_CFG_SRCCIV         4
+#define QM_CI_SCHED_CFG_SRQ_W          3
+#define QM_CI_SCHED_CFG_RW_W           2
+#define QM_CI_SCHED_CFG_BMAN_W         2
+/* write SRCCIV enable */
+#define QM_CI_SCHED_CFG_SRCCIV_EN      BIT(31)
+
+/* Follows WQ_CS_CFG0-5 */
+enum qm_wq_class {
+       qm_wq_portal = 0,
+       qm_wq_pool = 1,
+       qm_wq_fman0 = 2,
+       qm_wq_fman1 = 3,
+       qm_wq_caam = 4,
+       qm_wq_pme = 5,
+       qm_wq_first = qm_wq_portal,
+       qm_wq_last = qm_wq_pme
+};
+
+/* Follows FQD_[BARE|BAR|AR] and PFDR_[BARE|BAR|AR] */
+enum qm_memory {
+       qm_memory_fqd,
+       qm_memory_pfdr
+};
+
+/* Used by all error interrupt registers except 'inhibit' */
+#define QM_EIRQ_CIDE   0x20000000      /* Corenet Initiator Data Error */
+#define QM_EIRQ_CTDE   0x10000000      /* Corenet Target Data Error */
+#define QM_EIRQ_CITT   0x08000000      /* Corenet Invalid Target Transaction */
+#define QM_EIRQ_PLWI   0x04000000      /* PFDR Low Watermark */
+#define QM_EIRQ_MBEI   0x02000000      /* Multi-bit ECC Error */
+#define QM_EIRQ_SBEI   0x01000000      /* Single-bit ECC Error */
+#define QM_EIRQ_PEBI   0x00800000      /* PFDR Enqueues Blocked Interrupt */
+#define QM_EIRQ_IFSI   0x00020000      /* Invalid FQ Flow Control State */
+#define QM_EIRQ_ICVI   0x00010000      /* Invalid Command Verb */
+#define QM_EIRQ_IDDI   0x00000800      /* Invalid Dequeue (Direct-connect) */
+#define QM_EIRQ_IDFI   0x00000400      /* Invalid Dequeue FQ */
+#define QM_EIRQ_IDSI   0x00000200      /* Invalid Dequeue Source */
+#define QM_EIRQ_IDQI   0x00000100      /* Invalid Dequeue Queue */
+#define QM_EIRQ_IECE   0x00000010      /* Invalid Enqueue Configuration */
+#define QM_EIRQ_IEOI   0x00000008      /* Invalid Enqueue Overflow */
+#define QM_EIRQ_IESI   0x00000004      /* Invalid Enqueue State */
+#define QM_EIRQ_IECI   0x00000002      /* Invalid Enqueue Channel */
+#define QM_EIRQ_IEQI   0x00000001      /* Invalid Enqueue Queue */
+
+/* QMAN_ECIR valid error bit */
+#define PORTAL_ECSR_ERR        (QM_EIRQ_IEQI | QM_EIRQ_IESI | QM_EIRQ_IEOI | \
+                        QM_EIRQ_IDQI | QM_EIRQ_IDSI | QM_EIRQ_IDFI | \
+                        QM_EIRQ_IDDI | QM_EIRQ_ICVI | QM_EIRQ_IFSI)
+#define FQID_ECSR_ERR  (QM_EIRQ_IEQI | QM_EIRQ_IECI | QM_EIRQ_IESI | \
+                        QM_EIRQ_IEOI | QM_EIRQ_IDQI | QM_EIRQ_IDFI | \
+                        QM_EIRQ_IFSI)
+
+struct qm_ecir {
+       u32 info; /* res[30-31], ptyp[29], pnum[24-28], fqid[0-23] */
+};
+
+static bool qm_ecir_is_dcp(const struct qm_ecir *p)
+{
+       return p->info & BIT(29);
+}
+
+static int qm_ecir_get_pnum(const struct qm_ecir *p)
+{
+       return (p->info >> 24) & 0x1f;
+}
+
+static int qm_ecir_get_fqid(const struct qm_ecir *p)
+{
+       return p->info & (BIT(24) - 1);
+}
+
+struct qm_ecir2 {
+       u32 info; /* ptyp[31], res[10-30], pnum[0-9] */
+};
+
+static bool qm_ecir2_is_dcp(const struct qm_ecir2 *p)
+{
+       return p->info & BIT(31);
+}
+
+static int qm_ecir2_get_pnum(const struct qm_ecir2 *p)
+{
+       return p->info & (BIT(10) - 1);
+}
+
+struct qm_eadr {
+       u32 info; /* memid[24-27], eadr[0-11] */
+                 /* v3: memid[24-28], eadr[0-15] */
+};
+
+static int qm_eadr_get_memid(const struct qm_eadr *p)
+{
+       return (p->info >> 24) & 0xf;
+}
+
+static int qm_eadr_get_eadr(const struct qm_eadr *p)
+{
+       return p->info & (BIT(12) - 1);
+}
+
+static int qm_eadr_v3_get_memid(const struct qm_eadr *p)
+{
+       return (p->info >> 24) & 0x1f;
+}
+
+static int qm_eadr_v3_get_eadr(const struct qm_eadr *p)
+{
+       return p->info & (BIT(16) - 1);
+}
+
+struct qman_hwerr_txt {
+       u32 mask;
+       const char *txt;
+};
+
+
+static const struct qman_hwerr_txt qman_hwerr_txts[] = {
+       { QM_EIRQ_CIDE, "Corenet Initiator Data Error" },
+       { QM_EIRQ_CTDE, "Corenet Target Data Error" },
+       { QM_EIRQ_CITT, "Corenet Invalid Target Transaction" },
+       { QM_EIRQ_PLWI, "PFDR Low Watermark" },
+       { QM_EIRQ_MBEI, "Multi-bit ECC Error" },
+       { QM_EIRQ_SBEI, "Single-bit ECC Error" },
+       { QM_EIRQ_PEBI, "PFDR Enqueues Blocked Interrupt" },
+       { QM_EIRQ_ICVI, "Invalid Command Verb" },
+       { QM_EIRQ_IFSI, "Invalid Flow Control State" },
+       { QM_EIRQ_IDDI, "Invalid Dequeue (Direct-connect)" },
+       { QM_EIRQ_IDFI, "Invalid Dequeue FQ" },
+       { QM_EIRQ_IDSI, "Invalid Dequeue Source" },
+       { QM_EIRQ_IDQI, "Invalid Dequeue Queue" },
+       { QM_EIRQ_IECE, "Invalid Enqueue Configuration" },
+       { QM_EIRQ_IEOI, "Invalid Enqueue Overflow" },
+       { QM_EIRQ_IESI, "Invalid Enqueue State" },
+       { QM_EIRQ_IECI, "Invalid Enqueue Channel" },
+       { QM_EIRQ_IEQI, "Invalid Enqueue Queue" },
+};
+
+struct qman_error_info_mdata {
+       u16 addr_mask;
+       u16 bits;
+       const char *txt;
+};
+
+static const struct qman_error_info_mdata error_mdata[] = {
+       { 0x01FF, 24, "FQD cache tag memory 0" },
+       { 0x01FF, 24, "FQD cache tag memory 1" },
+       { 0x01FF, 24, "FQD cache tag memory 2" },
+       { 0x01FF, 24, "FQD cache tag memory 3" },
+       { 0x0FFF, 512, "FQD cache memory" },
+       { 0x07FF, 128, "SFDR memory" },
+       { 0x01FF, 72, "WQ context memory" },
+       { 0x00FF, 240, "CGR memory" },
+       { 0x00FF, 302, "Internal Order Restoration List memory" },
+       { 0x01FF, 256, "SW portal ring memory" },
+};
+
+#define QMAN_ERRS_TO_DISABLE (QM_EIRQ_PLWI | QM_EIRQ_PEBI)
+
+/*
+ * TODO: unimplemented registers
+ *
+ * Keeping a list here of QMan registers I have not yet covered;
+ * QCSP_DD_IHRSR, QCSP_DD_IHRFR, QCSP_DD_HASR,
+ * DCP_DD_IHRSR, DCP_DD_IHRFR, DCP_DD_HASR, CM_CFG,
+ * QMAN_EECC, QMAN_SBET, QMAN_EINJ, QMAN_SBEC0-12
+ */
+
+/* Pointer to the start of the QMan's CCSR space */
+static u32 __iomem *qm_ccsr_start;
+/* A SDQCR mask comprising all the available/visible pool channels */
+static u32 qm_pools_sdqcr;
+
+static inline u32 qm_ccsr_in(u32 offset)
+{
+       return ioread32be(qm_ccsr_start + offset/4);
+}
+
+static inline void qm_ccsr_out(u32 offset, u32 val)
+{
+       iowrite32be(val, qm_ccsr_start + offset/4);
+}
+
+u32 qm_get_pools_sdqcr(void)
+{
+       return qm_pools_sdqcr;
+}
+
+enum qm_dc_portal {
+       qm_dc_portal_fman0 = 0,
+       qm_dc_portal_fman1 = 1
+};
+
+static void qm_set_dc(enum qm_dc_portal portal, int ed, u8 sernd)
+{
+       DPAA_ASSERT(!ed || portal == qm_dc_portal_fman0 ||
+                   portal == qm_dc_portal_fman1);
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
+               qm_ccsr_out(REG_DCP_CFG(portal),
+                           (ed ? 0x1000 : 0) | (sernd & 0x3ff));
+       else
+               qm_ccsr_out(REG_DCP_CFG(portal),
+                           (ed ? 0x100 : 0) | (sernd & 0x1f));
+}
+
+static void qm_set_wq_scheduling(enum qm_wq_class wq_class,
+                                u8 cs_elev, u8 csw2, u8 csw3, u8 csw4,
+                                u8 csw5, u8 csw6, u8 csw7)
+{
+       qm_ccsr_out(REG_WQ_CS_CFG(wq_class), ((cs_elev & 0xff) << 24) |
+                   ((csw2 & 0x7) << 20) | ((csw3 & 0x7) << 16) |
+                   ((csw4 & 0x7) << 12) | ((csw5 & 0x7) << 8) |
+                   ((csw6 & 0x7) << 4) | (csw7 & 0x7));
+}
+
+static void qm_set_hid(void)
+{
+       qm_ccsr_out(REG_HID_CFG, 0);
+}
+
+static void qm_set_corenet_initiator(void)
+{
+       qm_ccsr_out(REG_CI_SCHED_CFG, QM_CI_SCHED_CFG_SRCCIV_EN |
+                   (QM_CI_SCHED_CFG_SRCCIV << 24) |
+                   (QM_CI_SCHED_CFG_SRQ_W << 8) |
+                   (QM_CI_SCHED_CFG_RW_W << 4) |
+                   QM_CI_SCHED_CFG_BMAN_W);
+}
+
+static void qm_get_version(u16 *id, u8 *major, u8 *minor)
+{
+       u32 v = qm_ccsr_in(REG_IP_REV_1);
+       *id = (v >> 16);
+       *major = (v >> 8) & 0xff;
+       *minor = v & 0xff;
+}
+
+#define PFDR_AR_EN             BIT(31)
+static void qm_set_memory(enum qm_memory memory, u64 ba, u32 size)
+{
+       u32 offset = (memory == qm_memory_fqd) ? REG_FQD_BARE : REG_PFDR_BARE;
+       u32 exp = ilog2(size);
+
+       /* choke if size isn't within range */
+       DPAA_ASSERT((size >= 4096) && (size <= 1024*1024*1024) &&
+                   is_power_of_2(size));
+       /* choke if 'ba' has lower-alignment than 'size' */
+       DPAA_ASSERT(!(ba & (size - 1)));
+       qm_ccsr_out(offset, upper_32_bits(ba));
+       qm_ccsr_out(offset + REG_offset_BAR, lower_32_bits(ba));
+       qm_ccsr_out(offset + REG_offset_AR, PFDR_AR_EN | (exp - 1));
+}
+
+static void qm_set_pfdr_threshold(u32 th, u8 k)
+{
+       qm_ccsr_out(REG_PFDR_FP_LWIT, th & 0xffffff);
+       qm_ccsr_out(REG_PFDR_CFG, k);
+}
+
+static void qm_set_sfdr_threshold(u16 th)
+{
+       qm_ccsr_out(REG_SFDR_CFG, th & 0x3ff);
+}
+
+static int qm_init_pfdr(struct device *dev, u32 pfdr_start, u32 num)
+{
+       u8 rslt = MCR_get_rslt(qm_ccsr_in(REG_MCR));
+
+       DPAA_ASSERT(pfdr_start && !(pfdr_start & 7) && !(num & 7) && num);
+       /* Make sure the command interface is 'idle' */
+       if (!MCR_rslt_idle(rslt)) {
+               dev_crit(dev, "QMAN_MCR isn't idle");
+               WARN_ON(1);
+       }
+
+       /* Write the MCR command params then the verb */
+       qm_ccsr_out(REG_MCP(0), pfdr_start);
+       /*
+        * TODO: remove this - it's a workaround for a model bug that is
+        * corrected in more recent versions. We use the workaround until
+        * everyone has upgraded.
+        */
+       qm_ccsr_out(REG_MCP(1), pfdr_start + num - 16);
+       dma_wmb();
+       qm_ccsr_out(REG_MCR, MCR_INIT_PFDR);
+       /* Poll for the result */
+       do {
+               rslt = MCR_get_rslt(qm_ccsr_in(REG_MCR));
+       } while (!MCR_rslt_idle(rslt));
+       if (MCR_rslt_ok(rslt))
+               return 0;
+       if (MCR_rslt_eaccess(rslt))
+               return -EACCES;
+       if (MCR_rslt_inval(rslt))
+               return -EINVAL;
+       dev_crit(dev, "Unexpected result from MCR_INIT_PFDR: %02x\n", rslt);
+       return -ENODEV;
+}
+
+/*
+ * Ideally we would use the DMA API to turn rmem->base into a DMA address
+ * (especially if iommu translations ever get involved).  Unfortunately, the
+ * DMA API currently does not allow mapping anything that is not backed with
+ * a struct page.
+ */
+static dma_addr_t fqd_a, pfdr_a;
+static size_t fqd_sz, pfdr_sz;
+
+static int qman_fqd(struct reserved_mem *rmem)
+{
+       fqd_a = rmem->base;
+       fqd_sz = rmem->size;
+
+       WARN_ON(!(fqd_a && fqd_sz));
+
+       return 0;
+}
+RESERVEDMEM_OF_DECLARE(qman_fqd, "fsl,qman-fqd", qman_fqd);
+
+static int qman_pfdr(struct reserved_mem *rmem)
+{
+       pfdr_a = rmem->base;
+       pfdr_sz = rmem->size;
+
+       WARN_ON(!(pfdr_a && pfdr_sz));
+
+       return 0;
+}
+RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr);
+
+static unsigned int qm_get_fqid_maxcnt(void)
+{
+       return fqd_sz / 64;
+}
+
+/*
+ * Flush this memory range from data cache so that QMAN originated
+ * transactions for this memory region could be marked non-coherent.
+ */
+static int zero_priv_mem(struct device *dev, struct device_node *node,
+                        phys_addr_t addr, size_t sz)
+{
+       /* map as cacheable, non-guarded */
+       void __iomem *tmpp = ioremap_prot(addr, sz, 0);
+
+       memset_io(tmpp, 0, sz);
+       flush_dcache_range((unsigned long)tmpp,
+                          (unsigned long)tmpp + sz);
+       iounmap(tmpp);
+
+       return 0;
+}
+
+static void log_edata_bits(struct device *dev, u32 bit_count)
+{
+       u32 i, j, mask = 0xffffffff;
+
+       dev_warn(dev, "ErrInt, EDATA:\n");
+       i = bit_count / 32;
+       if (bit_count % 32) {
+               i++;
+               mask = ~(mask << bit_count % 32);
+       }
+       j = 16 - i;
+       dev_warn(dev, "  0x%08x\n", qm_ccsr_in(REG_EDATA(j)) & mask);
+       j++;
+       for (; j < 16; j++)
+               dev_warn(dev, "  0x%08x\n", qm_ccsr_in(REG_EDATA(j)));
+}
+
+static void log_additional_error_info(struct device *dev, u32 isr_val,
+                                     u32 ecsr_val)
+{
+       struct qm_ecir ecir_val;
+       struct qm_eadr eadr_val;
+       int memid;
+
+       ecir_val.info = qm_ccsr_in(REG_ECIR);
+       /* Is portal info valid */
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) {
+               struct qm_ecir2 ecir2_val;
+
+               ecir2_val.info = qm_ccsr_in(REG_ECIR2);
+               if (ecsr_val & PORTAL_ECSR_ERR) {
+                       dev_warn(dev, "ErrInt: %s id %d\n",
+                                qm_ecir2_is_dcp(&ecir2_val) ? "DCP" : "SWP",
+                                qm_ecir2_get_pnum(&ecir2_val));
+               }
+               if (ecsr_val & (FQID_ECSR_ERR | QM_EIRQ_IECE))
+                       dev_warn(dev, "ErrInt: ecir.fqid 0x%x\n",
+                                qm_ecir_get_fqid(&ecir_val));
+
+               if (ecsr_val & (QM_EIRQ_SBEI|QM_EIRQ_MBEI)) {
+                       eadr_val.info = qm_ccsr_in(REG_EADR);
+                       memid = qm_eadr_v3_get_memid(&eadr_val);
+                       dev_warn(dev, "ErrInt: EADR Memory: %s, 0x%x\n",
+                                error_mdata[memid].txt,
+                                error_mdata[memid].addr_mask
+                                       & qm_eadr_v3_get_eadr(&eadr_val));
+                       log_edata_bits(dev, error_mdata[memid].bits);
+               }
+       } else {
+               if (ecsr_val & PORTAL_ECSR_ERR) {
+                       dev_warn(dev, "ErrInt: %s id %d\n",
+                                qm_ecir_is_dcp(&ecir_val) ? "DCP" : "SWP",
+                                qm_ecir_get_pnum(&ecir_val));
+               }
+               if (ecsr_val & FQID_ECSR_ERR)
+                       dev_warn(dev, "ErrInt: ecir.fqid 0x%x\n",
+                                qm_ecir_get_fqid(&ecir_val));
+
+               if (ecsr_val & (QM_EIRQ_SBEI|QM_EIRQ_MBEI)) {
+                       eadr_val.info = qm_ccsr_in(REG_EADR);
+                       memid = qm_eadr_get_memid(&eadr_val);
+                       dev_warn(dev, "ErrInt: EADR Memory: %s, 0x%x\n",
+                                error_mdata[memid].txt,
+                                error_mdata[memid].addr_mask
+                                       & qm_eadr_get_eadr(&eadr_val));
+                       log_edata_bits(dev, error_mdata[memid].bits);
+               }
+       }
+}
+
+static irqreturn_t qman_isr(int irq, void *ptr)
+{
+       u32 isr_val, ier_val, ecsr_val, isr_mask, i;
+       struct device *dev = ptr;
+
+       ier_val = qm_ccsr_in(REG_ERR_IER);
+       isr_val = qm_ccsr_in(REG_ERR_ISR);
+       ecsr_val = qm_ccsr_in(REG_ECSR);
+       isr_mask = isr_val & ier_val;
+
+       if (!isr_mask)
+               return IRQ_NONE;
+
+       for (i = 0; i < ARRAY_SIZE(qman_hwerr_txts); i++) {
+               if (qman_hwerr_txts[i].mask & isr_mask) {
+                       dev_err_ratelimited(dev, "ErrInt: %s\n",
+                                           qman_hwerr_txts[i].txt);
+                       if (qman_hwerr_txts[i].mask & ecsr_val) {
+                               log_additional_error_info(dev, isr_mask,
+                                                         ecsr_val);
+                               /* Re-arm error capture registers */
+                               qm_ccsr_out(REG_ECSR, ecsr_val);
+                       }
+                       if (qman_hwerr_txts[i].mask & QMAN_ERRS_TO_DISABLE) {
+                               dev_dbg(dev, "Disabling error 0x%x\n",
+                                       qman_hwerr_txts[i].mask);
+                               ier_val &= ~qman_hwerr_txts[i].mask;
+                               qm_ccsr_out(REG_ERR_IER, ier_val);
+                       }
+               }
+       }
+       qm_ccsr_out(REG_ERR_ISR, isr_val);
+
+       return IRQ_HANDLED;
+}
+
+static int qman_init_ccsr(struct device *dev)
+{
+       int i, err;
+
+       /* FQD memory */
+       qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz);
+       /* PFDR memory */
+       qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz);
+       err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8);
+       if (err)
+               return err;
+       /* thresholds */
+       qm_set_pfdr_threshold(512, 64);
+       qm_set_sfdr_threshold(128);
+       /* clear stale PEBI bit from interrupt status register */
+       qm_ccsr_out(REG_ERR_ISR, QM_EIRQ_PEBI);
+       /* corenet initiator settings */
+       qm_set_corenet_initiator();
+       /* HID settings */
+       qm_set_hid();
+       /* Set scheduling weights to defaults */
+       for (i = qm_wq_first; i <= qm_wq_last; i++)
+               qm_set_wq_scheduling(i, 0, 0, 0, 0, 0, 0, 0);
+       /* We are not prepared to accept ERNs for hardware enqueues */
+       qm_set_dc(qm_dc_portal_fman0, 1, 0);
+       qm_set_dc(qm_dc_portal_fman1, 1, 0);
+       return 0;
+}
+
+#define LIO_CFG_LIODN_MASK 0x0fff0000
+void qman_liodn_fixup(u16 channel)
+{
+       static int done;
+       static u32 liodn_offset;
+       u32 before, after;
+       int idx = channel - QM_CHANNEL_SWPORTAL0;
+
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
+               before = qm_ccsr_in(REG_REV3_QCSP_LIO_CFG(idx));
+       else
+               before = qm_ccsr_in(REG_QCSP_LIO_CFG(idx));
+       if (!done) {
+               liodn_offset = before & LIO_CFG_LIODN_MASK;
+               done = 1;
+               return;
+       }
+       after = (before & (~LIO_CFG_LIODN_MASK)) | liodn_offset;
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
+               qm_ccsr_out(REG_REV3_QCSP_LIO_CFG(idx), after);
+       else
+               qm_ccsr_out(REG_QCSP_LIO_CFG(idx), after);
+}
+
+#define IO_CFG_SDEST_MASK 0x00ff0000
+void qman_set_sdest(u16 channel, unsigned int cpu_idx)
+{
+       int idx = channel - QM_CHANNEL_SWPORTAL0;
+       u32 before, after;
+
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) {
+               before = qm_ccsr_in(REG_REV3_QCSP_IO_CFG(idx));
+               /* Each pair of vcpu share the same SRQ(SDEST) */
+               cpu_idx /= 2;
+               after = (before & (~IO_CFG_SDEST_MASK)) | (cpu_idx << 16);
+               qm_ccsr_out(REG_REV3_QCSP_IO_CFG(idx), after);
+       } else {
+               before = qm_ccsr_in(REG_QCSP_IO_CFG(idx));
+               after = (before & (~IO_CFG_SDEST_MASK)) | (cpu_idx << 16);
+               qm_ccsr_out(REG_QCSP_IO_CFG(idx), after);
+       }
+}
+
+static int qman_resource_init(struct device *dev)
+{
+       int pool_chan_num, cgrid_num;
+       int ret, i;
+
+       switch (qman_ip_rev >> 8) {
+       case 1:
+               pool_chan_num = 15;
+               cgrid_num = 256;
+               break;
+       case 2:
+               pool_chan_num = 3;
+               cgrid_num = 64;
+               break;
+       case 3:
+               pool_chan_num = 15;
+               cgrid_num = 256;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       ret = gen_pool_add(qm_qpalloc, qm_channel_pool1 | DPAA_GENALLOC_OFF,
+                          pool_chan_num, -1);
+       if (ret) {
+               dev_err(dev, "Failed to seed pool channels (%d)\n", ret);
+               return ret;
+       }
+
+       ret = gen_pool_add(qm_cgralloc, DPAA_GENALLOC_OFF, cgrid_num, -1);
+       if (ret) {
+               dev_err(dev, "Failed to seed CGRID range (%d)\n", ret);
+               return ret;
+       }
+
+       /* parse pool channels into the SDQCR mask */
+       for (i = 0; i < cgrid_num; i++)
+               qm_pools_sdqcr |= QM_SDQCR_CHANNELS_POOL_CONV(i);
+
+       ret = gen_pool_add(qm_fqalloc, QM_FQID_RANGE_START | DPAA_GENALLOC_OFF,
+                          qm_get_fqid_maxcnt() - QM_FQID_RANGE_START, -1);
+       if (ret) {
+               dev_err(dev, "Failed to seed FQID range (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int fsl_qman_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct resource *res;
+       int ret, err_irq;
+       u16 id;
+       u8 major, minor;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "Can't get %s property 'IORESOURCE_MEM'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+       qm_ccsr_start = devm_ioremap(dev, res->start, resource_size(res));
+       if (!qm_ccsr_start)
+               return -ENXIO;
+
+       qm_get_version(&id, &major, &minor);
+       if (major == 1 && minor == 0) {
+               dev_err(dev, "Rev1.0 on P4080 rev1 is not supported!\n");
+                       return -ENODEV;
+       } else if (major == 1 && minor == 1)
+               qman_ip_rev = QMAN_REV11;
+       else if (major == 1 && minor == 2)
+               qman_ip_rev = QMAN_REV12;
+       else if (major == 2 && minor == 0)
+               qman_ip_rev = QMAN_REV20;
+       else if (major == 3 && minor == 0)
+               qman_ip_rev = QMAN_REV30;
+       else if (major == 3 && minor == 1)
+               qman_ip_rev = QMAN_REV31;
+       else {
+               dev_err(dev, "Unknown QMan version\n");
+               return -ENODEV;
+       }
+
+       if ((qman_ip_rev & 0xff00) >= QMAN_REV30)
+               qm_channel_pool1 = QMAN_CHANNEL_POOL1_REV3;
+
+       ret = zero_priv_mem(dev, node, fqd_a, fqd_sz);
+       WARN_ON(ret);
+       if (ret)
+               return -ENODEV;
+
+       ret = qman_init_ccsr(dev);
+       if (ret) {
+               dev_err(dev, "CCSR setup failed\n");
+               return ret;
+       }
+
+       err_irq = platform_get_irq(pdev, 0);
+       if (err_irq <= 0) {
+               dev_info(dev, "Can't get %s property 'interrupts'\n",
+                        node->full_name);
+               return -ENODEV;
+       }
+       ret = devm_request_irq(dev, err_irq, qman_isr, IRQF_SHARED, "qman-err",
+                              dev);
+       if (ret)  {
+               dev_err(dev, "devm_request_irq() failed %d for '%s'\n",
+                       ret, node->full_name);
+               return ret;
+       }
+
+       /*
+        * Write-to-clear any stale bits, (eg. starvation being asserted prior
+        * to resource allocation during driver init).
+        */
+       qm_ccsr_out(REG_ERR_ISR, 0xffffffff);
+       /* Enable Error Interrupts */
+       qm_ccsr_out(REG_ERR_IER, 0xffffffff);
+
+       qm_fqalloc = devm_gen_pool_create(dev, 0, -1, "qman-fqalloc");
+       if (IS_ERR(qm_fqalloc)) {
+               ret = PTR_ERR(qm_fqalloc);
+               dev_err(dev, "qman-fqalloc pool init failed (%d)\n", ret);
+               return ret;
+       }
+
+       qm_qpalloc = devm_gen_pool_create(dev, 0, -1, "qman-qpalloc");
+       if (IS_ERR(qm_qpalloc)) {
+               ret = PTR_ERR(qm_qpalloc);
+               dev_err(dev, "qman-qpalloc pool init failed (%d)\n", ret);
+               return ret;
+       }
+
+       qm_cgralloc = devm_gen_pool_create(dev, 0, -1, "qman-cgralloc");
+       if (IS_ERR(qm_cgralloc)) {
+               ret = PTR_ERR(qm_cgralloc);
+               dev_err(dev, "qman-cgralloc pool init failed (%d)\n", ret);
+               return ret;
+       }
+
+       ret = qman_resource_init(dev);
+       if (ret)
+               return ret;
+
+       ret = qman_alloc_fq_table(qm_get_fqid_maxcnt());
+       if (ret)
+               return ret;
+
+       ret = qman_wq_alloc();
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static const struct of_device_id fsl_qman_ids[] = {
+       {
+               .compatible = "fsl,qman",
+       },
+       {}
+};
+
+static struct platform_driver fsl_qman_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = fsl_qman_ids,
+               .suppress_bind_attrs = true,
+       },
+       .probe = fsl_qman_probe,
+};
+
+builtin_platform_driver(fsl_qman_driver);
diff --git a/drivers/soc/fsl/qbman/qman_portal.c b/drivers/soc/fsl/qbman/qman_portal.c
new file mode 100644 (file)
index 0000000..1486143
--- /dev/null
@@ -0,0 +1,355 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_priv.h"
+
+/* Enable portal interupts (as opposed to polling mode) */
+#define CONFIG_FSL_DPA_PIRQ_SLOW  1
+#define CONFIG_FSL_DPA_PIRQ_FAST  1
+
+static struct cpumask portal_cpus;
+/* protect qman global registers and global data shared among portals */
+static DEFINE_SPINLOCK(qman_lock);
+
+static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu)
+{
+#ifdef CONFIG_FSL_PAMU
+       struct device *dev = pcfg->dev;
+       int window_count = 1;
+       struct iommu_domain_geometry geom_attr;
+       struct pamu_stash_attribute stash_attr;
+       int ret;
+
+       pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type);
+       if (!pcfg->iommu_domain) {
+               dev_err(dev, "%s(): iommu_domain_alloc() failed", __func__);
+               goto no_iommu;
+       }
+       geom_attr.aperture_start = 0;
+       geom_attr.aperture_end =
+               ((dma_addr_t)1 << min(8 * sizeof(dma_addr_t), (size_t)36)) - 1;
+       geom_attr.force_aperture = true;
+       ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_GEOMETRY,
+                                   &geom_attr);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
+                       ret);
+               goto out_domain_free;
+       }
+       ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_WINDOWS,
+                                   &window_count);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
+                       ret);
+               goto out_domain_free;
+       }
+       stash_attr.cpu = cpu;
+       stash_attr.cache = PAMU_ATTR_CACHE_L1;
+       ret = iommu_domain_set_attr(pcfg->iommu_domain,
+                                   DOMAIN_ATTR_FSL_PAMU_STASH,
+                                   &stash_attr);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_domain_set_attr() = %d",
+                       __func__, ret);
+               goto out_domain_free;
+       }
+       ret = iommu_domain_window_enable(pcfg->iommu_domain, 0, 0, 1ULL << 36,
+                                        IOMMU_READ | IOMMU_WRITE);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_domain_window_enable() = %d",
+                       __func__, ret);
+               goto out_domain_free;
+       }
+       ret = iommu_attach_device(pcfg->iommu_domain, dev);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_device_attach() = %d", __func__,
+                       ret);
+               goto out_domain_free;
+       }
+       ret = iommu_domain_set_attr(pcfg->iommu_domain,
+                                   DOMAIN_ATTR_FSL_PAMU_ENABLE,
+                                   &window_count);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
+                       ret);
+               goto out_detach_device;
+       }
+
+no_iommu:
+#endif
+       qman_set_sdest(pcfg->channel, cpu);
+
+       return;
+
+#ifdef CONFIG_FSL_PAMU
+out_detach_device:
+       iommu_detach_device(pcfg->iommu_domain, NULL);
+out_domain_free:
+       iommu_domain_free(pcfg->iommu_domain);
+       pcfg->iommu_domain = NULL;
+#endif
+}
+
+static struct qman_portal *init_pcfg(struct qm_portal_config *pcfg)
+{
+       struct qman_portal *p;
+       u32 irq_sources = 0;
+
+       /* We need the same LIODN offset for all portals */
+       qman_liodn_fixup(pcfg->channel);
+
+       pcfg->iommu_domain = NULL;
+       portal_set_cpu(pcfg, pcfg->cpu);
+
+       p = qman_create_affine_portal(pcfg, NULL);
+       if (!p) {
+               dev_crit(pcfg->dev, "%s: Portal failure on cpu %d\n",
+                        __func__, pcfg->cpu);
+               return NULL;
+       }
+
+       /* Determine what should be interrupt-vs-poll driven */
+#ifdef CONFIG_FSL_DPA_PIRQ_SLOW
+       irq_sources |= QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI |
+                      QM_PIRQ_CSCI;
+#endif
+#ifdef CONFIG_FSL_DPA_PIRQ_FAST
+       irq_sources |= QM_PIRQ_DQRI;
+#endif
+       qman_p_irqsource_add(p, irq_sources);
+
+       spin_lock(&qman_lock);
+       if (cpumask_equal(&portal_cpus, cpu_possible_mask)) {
+               /* all assigned portals are initialized now */
+               qman_init_cgr_all();
+       }
+       spin_unlock(&qman_lock);
+
+       dev_info(pcfg->dev, "Portal initialised, cpu %d\n", pcfg->cpu);
+
+       return p;
+}
+
+static void qman_portal_update_sdest(const struct qm_portal_config *pcfg,
+                                                       unsigned int cpu)
+{
+#ifdef CONFIG_FSL_PAMU /* TODO */
+       struct pamu_stash_attribute stash_attr;
+       int ret;
+
+       if (pcfg->iommu_domain) {
+               stash_attr.cpu = cpu;
+               stash_attr.cache = PAMU_ATTR_CACHE_L1;
+               ret = iommu_domain_set_attr(pcfg->iommu_domain,
+                               DOMAIN_ATTR_FSL_PAMU_STASH, &stash_attr);
+               if (ret < 0) {
+                       dev_err(pcfg->dev,
+                               "Failed to update pamu stash setting\n");
+                       return;
+               }
+       }
+#endif
+       qman_set_sdest(pcfg->channel, cpu);
+}
+
+static void qman_offline_cpu(unsigned int cpu)
+{
+       struct qman_portal *p;
+       const struct qm_portal_config *pcfg;
+
+       p = affine_portals[cpu];
+       if (p) {
+               pcfg = qman_get_qm_portal_config(p);
+               if (pcfg) {
+                       irq_set_affinity(pcfg->irq, cpumask_of(0));
+                       qman_portal_update_sdest(pcfg, 0);
+               }
+       }
+}
+
+static void qman_online_cpu(unsigned int cpu)
+{
+       struct qman_portal *p;
+       const struct qm_portal_config *pcfg;
+
+       p = affine_portals[cpu];
+       if (p) {
+               pcfg = qman_get_qm_portal_config(p);
+               if (pcfg) {
+                       irq_set_affinity(pcfg->irq, cpumask_of(cpu));
+                       qman_portal_update_sdest(pcfg, cpu);
+               }
+       }
+}
+
+static int qman_hotplug_cpu_callback(struct notifier_block *nfb,
+                                    unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               qman_online_cpu(cpu);
+               break;
+       case CPU_DOWN_PREPARE:
+       case CPU_DOWN_PREPARE_FROZEN:
+               qman_offline_cpu(cpu);
+       default:
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block qman_hotplug_cpu_notifier = {
+       .notifier_call = qman_hotplug_cpu_callback,
+};
+
+static int qman_portal_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct qm_portal_config *pcfg;
+       struct resource *addr_phys[2];
+       const u32 *channel;
+       void __iomem *va;
+       int irq, len, cpu;
+
+       pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
+       if (!pcfg)
+               return -ENOMEM;
+
+       pcfg->dev = dev;
+
+       addr_phys[0] = platform_get_resource(pdev, IORESOURCE_MEM,
+                                            DPAA_PORTAL_CE);
+       if (!addr_phys[0]) {
+               dev_err(dev, "Can't get %s property 'reg::CE'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+
+       addr_phys[1] = platform_get_resource(pdev, IORESOURCE_MEM,
+                                            DPAA_PORTAL_CI);
+       if (!addr_phys[1]) {
+               dev_err(dev, "Can't get %s property 'reg::CI'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+
+       channel = of_get_property(node, "cell-index", &len);
+       if (!channel || (len != 4)) {
+               dev_err(dev, "Can't get %s property 'cell-index'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+       pcfg->channel = *channel;
+       pcfg->cpu = -1;
+       irq = platform_get_irq(pdev, 0);
+       if (irq <= 0) {
+               dev_err(dev, "Can't get %s IRQ\n", node->full_name);
+               return -ENXIO;
+       }
+       pcfg->irq = irq;
+
+       va = ioremap_prot(addr_phys[0]->start, resource_size(addr_phys[0]), 0);
+       if (!va)
+               goto err_ioremap1;
+
+       pcfg->addr_virt[DPAA_PORTAL_CE] = va;
+
+       va = ioremap_prot(addr_phys[1]->start, resource_size(addr_phys[1]),
+                         _PAGE_GUARDED | _PAGE_NO_CACHE);
+       if (!va)
+               goto err_ioremap2;
+
+       pcfg->addr_virt[DPAA_PORTAL_CI] = va;
+
+       pcfg->pools = qm_get_pools_sdqcr();
+
+       spin_lock(&qman_lock);
+       cpu = cpumask_next_zero(-1, &portal_cpus);
+       if (cpu >= nr_cpu_ids) {
+               /* unassigned portal, skip init */
+               spin_unlock(&qman_lock);
+               return 0;
+       }
+
+       cpumask_set_cpu(cpu, &portal_cpus);
+       spin_unlock(&qman_lock);
+       pcfg->cpu = cpu;
+
+       if (!init_pcfg(pcfg))
+               goto err_ioremap2;
+
+       /* clear irq affinity if assigned cpu is offline */
+       if (!cpu_online(cpu))
+               qman_offline_cpu(cpu);
+
+       return 0;
+
+err_ioremap2:
+       iounmap(pcfg->addr_virt[DPAA_PORTAL_CE]);
+err_ioremap1:
+       dev_err(dev, "ioremap failed\n");
+       return -ENXIO;
+}
+
+static const struct of_device_id qman_portal_ids[] = {
+       {
+               .compatible = "fsl,qman-portal",
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, qman_portal_ids);
+
+static struct platform_driver qman_portal_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = qman_portal_ids,
+       },
+       .probe = qman_portal_probe,
+};
+
+static int __init qman_portal_driver_register(struct platform_driver *drv)
+{
+       int ret;
+
+       ret = platform_driver_register(drv);
+       if (ret < 0)
+               return ret;
+
+       register_hotcpu_notifier(&qman_hotplug_cpu_notifier);
+
+       return 0;
+}
+
+module_driver(qman_portal_driver,
+             qman_portal_driver_register, platform_driver_unregister);
diff --git a/drivers/soc/fsl/qbman/qman_priv.h b/drivers/soc/fsl/qbman/qman_priv.h
new file mode 100644 (file)
index 0000000..5cf821e
--- /dev/null
@@ -0,0 +1,371 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "dpaa_sys.h"
+
+#include <soc/fsl/qman.h>
+#include <linux/iommu.h>
+
+#if defined(CONFIG_FSL_PAMU)
+#include <asm/fsl_pamu_stash.h>
+#endif
+
+struct qm_mcr_querywq {
+       u8 verb;
+       u8 result;
+       u16 channel_wq; /* ignores wq (3 lsbits): _res[0-2] */
+       u8 __reserved[28];
+       u32 wq_len[8];
+} __packed;
+
+static inline u16 qm_mcr_querywq_get_chan(const struct qm_mcr_querywq *wq)
+{
+       return wq->channel_wq >> 3;
+}
+
+struct __qm_mcr_querycongestion {
+       u32 state[8];
+};
+
+/* "Query Congestion Group State" */
+struct qm_mcr_querycongestion {
+       u8 verb;
+       u8 result;
+       u8 __reserved[30];
+       /* Access this struct using qman_cgrs_get() */
+       struct __qm_mcr_querycongestion state;
+} __packed;
+
+/* "Query CGR" */
+struct qm_mcr_querycgr {
+       u8 verb;
+       u8 result;
+       u16 __reserved1;
+       struct __qm_mc_cgr cgr; /* CGR fields */
+       u8 __reserved2[6];
+       u8 i_bcnt_hi;   /* high 8-bits of 40-bit "Instant" */
+       u32 i_bcnt_lo;  /* low 32-bits of 40-bit */
+       u8 __reserved3[3];
+       u8 a_bcnt_hi;   /* high 8-bits of 40-bit "Average" */
+       u32 a_bcnt_lo;  /* low 32-bits of 40-bit */
+       u32 cscn_targ_swp[4];
+} __packed;
+
+static inline u64 qm_mcr_querycgr_i_get64(const struct qm_mcr_querycgr *q)
+{
+       return ((u64)q->i_bcnt_hi << 32) | (u64)q->i_bcnt_lo;
+}
+static inline u64 qm_mcr_querycgr_a_get64(const struct qm_mcr_querycgr *q)
+{
+       return ((u64)q->a_bcnt_hi << 32) | (u64)q->a_bcnt_lo;
+}
+
+/* "Query FQ Non-Programmable Fields" */
+struct qm_mcc_queryfq_np {
+       u8 _ncw_verb;
+       u8 __reserved1[3];
+       u32 fqid;       /* 24-bit */
+       u8 __reserved2[56];
+} __packed;
+
+struct qm_mcr_queryfq_np {
+       u8 verb;
+       u8 result;
+       u8 __reserved1;
+       u8 state;               /* QM_MCR_NP_STATE_*** */
+       u32 fqd_link;           /* 24-bit, _res2[24-31] */
+       u16 odp_seq;            /* 14-bit, _res3[14-15] */
+       u16 orp_nesn;           /* 14-bit, _res4[14-15] */
+       u16 orp_ea_hseq;        /* 15-bit, _res5[15] */
+       u16 orp_ea_tseq;        /* 15-bit, _res6[15] */
+       u32 orp_ea_hptr;        /* 24-bit, _res7[24-31] */
+       u32 orp_ea_tptr;        /* 24-bit, _res8[24-31] */
+       u32 pfdr_hptr;          /* 24-bit, _res9[24-31] */
+       u32 pfdr_tptr;          /* 24-bit, _res10[24-31] */
+       u8 __reserved2[5];
+       u8 is;                  /* 1-bit, _res12[1-7] */
+       u16 ics_surp;
+       u32 byte_cnt;
+       u32 frm_cnt;            /* 24-bit, _res13[24-31] */
+       u32 __reserved3;
+       u16 ra1_sfdr;           /* QM_MCR_NP_RA1_*** */
+       u16 ra2_sfdr;           /* QM_MCR_NP_RA2_*** */
+       u16 __reserved4;
+       u16 od1_sfdr;           /* QM_MCR_NP_OD1_*** */
+       u16 od2_sfdr;           /* QM_MCR_NP_OD2_*** */
+       u16 od3_sfdr;           /* QM_MCR_NP_OD3_*** */
+} __packed;
+
+#define QM_MCR_NP_STATE_FE             0x10
+#define QM_MCR_NP_STATE_R              0x08
+#define QM_MCR_NP_STATE_MASK           0x07    /* Reads FQD::STATE; */
+#define QM_MCR_NP_STATE_OOS            0x00
+#define QM_MCR_NP_STATE_RETIRED                0x01
+#define QM_MCR_NP_STATE_TEN_SCHED      0x02
+#define QM_MCR_NP_STATE_TRU_SCHED      0x03
+#define QM_MCR_NP_STATE_PARKED         0x04
+#define QM_MCR_NP_STATE_ACTIVE         0x05
+#define QM_MCR_NP_PTR_MASK             0x07ff  /* for RA[12] & OD[123] */
+#define QM_MCR_NP_RA1_NRA(v)           (((v) >> 14) & 0x3)     /* FQD::NRA */
+#define QM_MCR_NP_RA2_IT(v)            (((v) >> 14) & 0x1)     /* FQD::IT */
+#define QM_MCR_NP_OD1_NOD(v)           (((v) >> 14) & 0x3)     /* FQD::NOD */
+#define QM_MCR_NP_OD3_NPC(v)           (((v) >> 14) & 0x3)     /* FQD::NPC */
+
+enum qm_mcr_queryfq_np_masks {
+       qm_mcr_fqd_link_mask = BIT(24)-1,
+       qm_mcr_odp_seq_mask = BIT(14)-1,
+       qm_mcr_orp_nesn_mask = BIT(14)-1,
+       qm_mcr_orp_ea_hseq_mask = BIT(15)-1,
+       qm_mcr_orp_ea_tseq_mask = BIT(15)-1,
+       qm_mcr_orp_ea_hptr_mask = BIT(24)-1,
+       qm_mcr_orp_ea_tptr_mask = BIT(24)-1,
+       qm_mcr_pfdr_hptr_mask = BIT(24)-1,
+       qm_mcr_pfdr_tptr_mask = BIT(24)-1,
+       qm_mcr_is_mask = BIT(1)-1,
+       qm_mcr_frm_cnt_mask = BIT(24)-1,
+};
+#define qm_mcr_np_get(np, field) \
+       ((np)->field & (qm_mcr_##field##_mask))
+
+/* Congestion Groups */
+
+/*
+ * This wrapper represents a bit-array for the state of the 256 QMan congestion
+ * groups. Is also used as a *mask* for congestion groups, eg. so we ignore
+ * those that don't concern us. We harness the structure and accessor details
+ * already used in the management command to query congestion groups.
+ */
+#define CGR_BITS_PER_WORD 5
+#define CGR_WORD(x)    ((x) >> CGR_BITS_PER_WORD)
+#define CGR_BIT(x)     (BIT(31) >> ((x) & 0x1f))
+#define CGR_NUM        (sizeof(struct __qm_mcr_querycongestion) << 3)
+
+struct qman_cgrs {
+       struct __qm_mcr_querycongestion q;
+};
+
+static inline void qman_cgrs_init(struct qman_cgrs *c)
+{
+       memset(c, 0, sizeof(*c));
+}
+
+static inline void qman_cgrs_fill(struct qman_cgrs *c)
+{
+       memset(c, 0xff, sizeof(*c));
+}
+
+static inline int qman_cgrs_get(struct qman_cgrs *c, u8 cgr)
+{
+       return c->q.state[CGR_WORD(cgr)] & CGR_BIT(cgr);
+}
+
+static inline void qman_cgrs_cp(struct qman_cgrs *dest,
+                               const struct qman_cgrs *src)
+{
+       *dest = *src;
+}
+
+static inline void qman_cgrs_and(struct qman_cgrs *dest,
+                       const struct qman_cgrs *a, const struct qman_cgrs *b)
+{
+       int ret;
+       u32 *_d = dest->q.state;
+       const u32 *_a = a->q.state;
+       const u32 *_b = b->q.state;
+
+       for (ret = 0; ret < 8; ret++)
+               *_d++ = *_a++ & *_b++;
+}
+
+static inline void qman_cgrs_xor(struct qman_cgrs *dest,
+                       const struct qman_cgrs *a, const struct qman_cgrs *b)
+{
+       int ret;
+       u32 *_d = dest->q.state;
+       const u32 *_a = a->q.state;
+       const u32 *_b = b->q.state;
+
+       for (ret = 0; ret < 8; ret++)
+               *_d++ = *_a++ ^ *_b++;
+}
+
+void qman_init_cgr_all(void);
+
+struct qm_portal_config {
+       /*
+        * Corenet portal addresses;
+        * [0]==cache-enabled, [1]==cache-inhibited.
+        */
+       void __iomem *addr_virt[2];
+       struct device *dev;
+       struct iommu_domain *iommu_domain;
+       /* Allow these to be joined in lists */
+       struct list_head list;
+       /* User-visible portal configuration settings */
+       /* portal is affined to this cpu */
+       int cpu;
+       /* portal interrupt line */
+       int irq;
+       /*
+        * the portal's dedicated channel id, used initialising
+        * frame queues to target this portal when scheduled
+        */
+       u16 channel;
+       /*
+        * mask of pool channels this portal has dequeue access to
+        * (using QM_SDQCR_CHANNELS_POOL(n) for the bitmask)
+        */
+       u32 pools;
+};
+
+/* Revision info (for errata and feature handling) */
+#define QMAN_REV11 0x0101
+#define QMAN_REV12 0x0102
+#define QMAN_REV20 0x0200
+#define QMAN_REV30 0x0300
+#define QMAN_REV31 0x0301
+extern u16 qman_ip_rev; /* 0 if uninitialised, otherwise QMAN_REVx */
+
+#define QM_FQID_RANGE_START 1 /* FQID 0 reserved for internal use */
+extern struct gen_pool *qm_fqalloc; /* FQID allocator */
+extern struct gen_pool *qm_qpalloc; /* pool-channel allocator */
+extern struct gen_pool *qm_cgralloc; /* CGR ID allocator */
+u32 qm_get_pools_sdqcr(void);
+
+int qman_wq_alloc(void);
+void qman_liodn_fixup(u16 channel);
+void qman_set_sdest(u16 channel, unsigned int cpu_idx);
+
+struct qman_portal *qman_create_affine_portal(
+                       const struct qm_portal_config *config,
+                       const struct qman_cgrs *cgrs);
+const struct qm_portal_config *qman_destroy_affine_portal(void);
+
+/*
+ * qman_query_fq - Queries FQD fields (via h/w query command)
+ * @fq: the frame queue object to be queried
+ * @fqd: storage for the queried FQD fields
+ */
+int qman_query_fq(struct qman_fq *fq, struct qm_fqd *fqd);
+
+/*
+ * For qman_volatile_dequeue(); Choose one PRECEDENCE. EXACT is optional. Use
+ * NUMFRAMES(n) (6-bit) or NUMFRAMES_TILLEMPTY to fill in the frame-count. Use
+ * FQID(n) to fill in the frame queue ID.
+ */
+#define QM_VDQCR_PRECEDENCE_VDQCR      0x0
+#define QM_VDQCR_PRECEDENCE_SDQCR      0x80000000
+#define QM_VDQCR_EXACT                 0x40000000
+#define QM_VDQCR_NUMFRAMES_MASK                0x3f000000
+#define QM_VDQCR_NUMFRAMES_SET(n)      (((n) & 0x3f) << 24)
+#define QM_VDQCR_NUMFRAMES_GET(n)      (((n) >> 24) & 0x3f)
+#define QM_VDQCR_NUMFRAMES_TILLEMPTY   QM_VDQCR_NUMFRAMES_SET(0)
+
+#define QMAN_VOLATILE_FLAG_WAIT             0x00000001 /* wait if VDQCR is in use */
+#define QMAN_VOLATILE_FLAG_WAIT_INT  0x00000002 /* if wait, interruptible? */
+#define QMAN_VOLATILE_FLAG_FINISH    0x00000004 /* wait till VDQCR completes */
+
+/*
+ * qman_volatile_dequeue - Issue a volatile dequeue command
+ * @fq: the frame queue object to dequeue from
+ * @flags: a bit-mask of QMAN_VOLATILE_FLAG_*** options
+ * @vdqcr: bit mask of QM_VDQCR_*** options, as per qm_dqrr_vdqcr_set()
+ *
+ * Attempts to lock access to the portal's VDQCR volatile dequeue functionality.
+ * The function will block and sleep if QMAN_VOLATILE_FLAG_WAIT is specified and
+ * the VDQCR is already in use, otherwise returns non-zero for failure. If
+ * QMAN_VOLATILE_FLAG_FINISH is specified, the function will only return once
+ * the VDQCR command has finished executing (ie. once the callback for the last
+ * DQRR entry resulting from the VDQCR command has been called). If not using
+ * the FINISH flag, completion can be determined either by detecting the
+ * presence of the QM_DQRR_STAT_UNSCHEDULED and QM_DQRR_STAT_DQCR_EXPIRED bits
+ * in the "stat" parameter passed to the FQ's dequeue callback, or by waiting
+ * for the QMAN_FQ_STATE_VDQCR bit to disappear.
+ */
+int qman_volatile_dequeue(struct qman_fq *fq, u32 flags, u32 vdqcr);
+
+int qman_alloc_fq_table(u32 num_fqids);
+
+/*   QMan s/w corenet portal, low-level i/face  */
+
+/*
+ * For qm_dqrr_sdqcr_set(); Choose one SOURCE. Choose one COUNT. Choose one
+ * dequeue TYPE. Choose TOKEN (8-bit).
+ * If SOURCE == CHANNELS,
+ *   Choose CHANNELS_DEDICATED and/or CHANNELS_POOL(n).
+ *   You can choose DEDICATED_PRECEDENCE if the portal channel should have
+ *   priority.
+ * If SOURCE == SPECIFICWQ,
+ *     Either select the work-queue ID with SPECIFICWQ_WQ(), or select the
+ *     channel (SPECIFICWQ_DEDICATED or SPECIFICWQ_POOL()) and specify the
+ *     work-queue priority (0-7) with SPECIFICWQ_WQ() - either way, you get the
+ *     same value.
+ */
+#define QM_SDQCR_SOURCE_CHANNELS       0x0
+#define QM_SDQCR_SOURCE_SPECIFICWQ     0x40000000
+#define QM_SDQCR_COUNT_EXACT1          0x0
+#define QM_SDQCR_COUNT_UPTO3           0x20000000
+#define QM_SDQCR_DEDICATED_PRECEDENCE  0x10000000
+#define QM_SDQCR_TYPE_MASK             0x03000000
+#define QM_SDQCR_TYPE_NULL             0x0
+#define QM_SDQCR_TYPE_PRIO_QOS         0x01000000
+#define QM_SDQCR_TYPE_ACTIVE_QOS       0x02000000
+#define QM_SDQCR_TYPE_ACTIVE           0x03000000
+#define QM_SDQCR_TOKEN_MASK            0x00ff0000
+#define QM_SDQCR_TOKEN_SET(v)          (((v) & 0xff) << 16)
+#define QM_SDQCR_TOKEN_GET(v)          (((v) >> 16) & 0xff)
+#define QM_SDQCR_CHANNELS_DEDICATED    0x00008000
+#define QM_SDQCR_SPECIFICWQ_MASK       0x000000f7
+#define QM_SDQCR_SPECIFICWQ_DEDICATED  0x00000000
+#define QM_SDQCR_SPECIFICWQ_POOL(n)    ((n) << 4)
+#define QM_SDQCR_SPECIFICWQ_WQ(n)      (n)
+
+/* For qm_dqrr_vdqcr_set(): use FQID(n) to fill in the frame queue ID */
+#define QM_VDQCR_FQID_MASK             0x00ffffff
+#define QM_VDQCR_FQID(n)               ((n) & QM_VDQCR_FQID_MASK)
+
+/*
+ * Used by all portal interrupt registers except 'inhibit'
+ * Channels with frame availability
+ */
+#define QM_PIRQ_DQAVAIL        0x0000ffff
+
+/* The DQAVAIL interrupt fields break down into these bits; */
+#define QM_DQAVAIL_PORTAL      0x8000          /* Portal channel */
+#define QM_DQAVAIL_POOL(n)     (0x8000 >> (n)) /* Pool channel, n==[1..15] */
+#define QM_DQAVAIL_MASK                0xffff
+/* This mask contains all the "irqsource" bits visible to API users */
+#define QM_PIRQ_VISIBLE        (QM_PIRQ_SLOW | QM_PIRQ_DQRI)
+
+extern struct qman_portal *affine_portals[NR_CPUS];
+const struct qm_portal_config *qman_get_qm_portal_config(
+                                               struct qman_portal *portal);
diff --git a/drivers/soc/fsl/qbman/qman_test.c b/drivers/soc/fsl/qbman/qman_test.c
new file mode 100644 (file)
index 0000000..18f7f02
--- /dev/null
@@ -0,0 +1,62 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_test.h"
+
+MODULE_AUTHOR("Geoff Thorpe");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("QMan testing");
+
+static int test_init(void)
+{
+       int loop = 1;
+       int err = 0;
+
+       while (loop--) {
+#ifdef CONFIG_FSL_QMAN_TEST_STASH
+               err = qman_test_stash();
+               if (err)
+                       break;
+#endif
+#ifdef CONFIG_FSL_QMAN_TEST_API
+               err = qman_test_api();
+               if (err)
+                       break;
+#endif
+       }
+       return err;
+}
+
+static void test_exit(void)
+{
+}
+
+module_init(test_init);
+module_exit(test_exit);
diff --git a/drivers/soc/fsl/qbman/qman_test.h b/drivers/soc/fsl/qbman/qman_test.h
new file mode 100644 (file)
index 0000000..d5f8cb2
--- /dev/null
@@ -0,0 +1,36 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_priv.h"
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+int qman_test_stash(void);
+int qman_test_api(void);
diff --git a/drivers/soc/fsl/qbman/qman_test_api.c b/drivers/soc/fsl/qbman/qman_test_api.c
new file mode 100644 (file)
index 0000000..6880ff1
--- /dev/null
@@ -0,0 +1,252 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_test.h"
+
+#define CGR_ID         27
+#define POOL_ID                2
+#define FQ_FLAGS       QMAN_FQ_FLAG_DYNAMIC_FQID
+#define NUM_ENQUEUES   10
+#define NUM_PARTIAL    4
+#define PORTAL_SDQCR   (QM_SDQCR_SOURCE_CHANNELS | \
+                       QM_SDQCR_TYPE_PRIO_QOS | \
+                       QM_SDQCR_TOKEN_SET(0x98) | \
+                       QM_SDQCR_CHANNELS_DEDICATED | \
+                       QM_SDQCR_CHANNELS_POOL(POOL_ID))
+#define PORTAL_OPAQUE  ((void *)0xf00dbeef)
+#define VDQCR_FLAGS    (QMAN_VOLATILE_FLAG_WAIT | QMAN_VOLATILE_FLAG_FINISH)
+
+static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *,
+                                       struct qman_fq *,
+                                       const struct qm_dqrr_entry *);
+static void cb_ern(struct qman_portal *, struct qman_fq *,
+                  const union qm_mr_entry *);
+static void cb_fqs(struct qman_portal *, struct qman_fq *,
+                  const union qm_mr_entry *);
+
+static struct qm_fd fd, fd_dq;
+static struct qman_fq fq_base = {
+       .cb.dqrr = cb_dqrr,
+       .cb.ern = cb_ern,
+       .cb.fqs = cb_fqs
+};
+static DECLARE_WAIT_QUEUE_HEAD(waitqueue);
+static int retire_complete, sdqcr_complete;
+
+/* Helpers for initialising and "incrementing" a frame descriptor */
+static void fd_init(struct qm_fd *fd)
+{
+       qm_fd_addr_set64(fd, 0xabdeadbeefLLU);
+       qm_fd_set_contig_big(fd, 0x0000ffff);
+       fd->cmd = 0xfeedf00d;
+}
+
+static void fd_inc(struct qm_fd *fd)
+{
+       u64 t = qm_fd_addr_get64(fd);
+       int z = t >> 40;
+       unsigned int len, off;
+       enum qm_fd_format fmt;
+
+       t <<= 1;
+       if (z)
+               t |= 1;
+       qm_fd_addr_set64(fd, t);
+
+       fmt = qm_fd_get_format(fd);
+       off = qm_fd_get_offset(fd);
+       len = qm_fd_get_length(fd);
+       len--;
+       qm_fd_set_param(fd, fmt, off, len);
+
+       fd->cmd++;
+}
+
+/* The only part of the 'fd' we can't memcmp() is the ppid */
+static int fd_cmp(const struct qm_fd *a, const struct qm_fd *b)
+{
+       int r = (qm_fd_addr_get64(a) == qm_fd_addr_get64(b)) ? 0 : -1;
+
+       if (!r) {
+               enum qm_fd_format fmt_a, fmt_b;
+
+               fmt_a = qm_fd_get_format(a);
+               fmt_b = qm_fd_get_format(b);
+               r = fmt_a - fmt_b;
+       }
+       if (!r)
+               r = a->cfg - b->cfg;
+       if (!r)
+               r = a->cmd - b->cmd;
+       return r;
+}
+
+/* test */
+static int do_enqueues(struct qman_fq *fq)
+{
+       unsigned int loop;
+       int err = 0;
+
+       for (loop = 0; loop < NUM_ENQUEUES; loop++) {
+               if (qman_enqueue(fq, &fd)) {
+                       pr_crit("qman_enqueue() failed\n");
+                       err = -EIO;
+               }
+               fd_inc(&fd);
+       }
+
+       return err;
+}
+
+int qman_test_api(void)
+{
+       unsigned int flags, frmcnt;
+       int err;
+       struct qman_fq *fq = &fq_base;
+
+       pr_info("%s(): Starting\n", __func__);
+       fd_init(&fd);
+       fd_init(&fd_dq);
+
+       /* Initialise (parked) FQ */
+       err = qman_create_fq(0, FQ_FLAGS, fq);
+       if (err) {
+               pr_crit("qman_create_fq() failed\n");
+               goto failed;
+       }
+       err = qman_init_fq(fq, QMAN_INITFQ_FLAG_LOCAL, NULL);
+       if (err) {
+               pr_crit("qman_init_fq() failed\n");
+               goto failed;
+       }
+       /* Do enqueues + VDQCR, twice. (Parked FQ) */
+       err = do_enqueues(fq);
+       if (err)
+               goto failed;
+       pr_info("VDQCR (till-empty);\n");
+       frmcnt = QM_VDQCR_NUMFRAMES_TILLEMPTY;
+       err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt);
+       if (err) {
+               pr_crit("qman_volatile_dequeue() failed\n");
+               goto failed;
+       }
+       err = do_enqueues(fq);
+       if (err)
+               goto failed;
+       pr_info("VDQCR (%d of %d);\n", NUM_PARTIAL, NUM_ENQUEUES);
+       frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_PARTIAL);
+       err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt);
+       if (err) {
+               pr_crit("qman_volatile_dequeue() failed\n");
+               goto failed;
+       }
+       pr_info("VDQCR (%d of %d);\n", NUM_ENQUEUES - NUM_PARTIAL,
+               NUM_ENQUEUES);
+       frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_ENQUEUES - NUM_PARTIAL);
+       err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt);
+       if (err) {
+               pr_err("qman_volatile_dequeue() failed\n");
+               goto failed;
+       }
+
+       err = do_enqueues(fq);
+       if (err)
+               goto failed;
+       pr_info("scheduled dequeue (till-empty)\n");
+       err = qman_schedule_fq(fq);
+       if (err) {
+               pr_crit("qman_schedule_fq() failed\n");
+               goto failed;
+       }
+       wait_event(waitqueue, sdqcr_complete);
+
+       /* Retire and OOS the FQ */
+       err = qman_retire_fq(fq, &flags);
+       if (err < 0) {
+               pr_crit("qman_retire_fq() failed\n");
+               goto failed;
+       }
+       wait_event(waitqueue, retire_complete);
+       if (flags & QMAN_FQ_STATE_BLOCKOOS) {
+               err = -EIO;
+               pr_crit("leaking frames\n");
+               goto failed;
+       }
+       err = qman_oos_fq(fq);
+       if (err) {
+               pr_crit("qman_oos_fq() failed\n");
+               goto failed;
+       }
+       qman_destroy_fq(fq);
+       pr_info("%s(): Finished\n", __func__);
+       return 0;
+
+failed:
+       WARN_ON(1);
+       return err;
+}
+
+static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *p,
+                                       struct qman_fq *fq,
+                                       const struct qm_dqrr_entry *dq)
+{
+       if (WARN_ON(fd_cmp(&fd_dq, &dq->fd))) {
+               pr_err("BADNESS: dequeued frame doesn't match;\n");
+               return qman_cb_dqrr_consume;
+       }
+       fd_inc(&fd_dq);
+       if (!(dq->stat & QM_DQRR_STAT_UNSCHEDULED) && !fd_cmp(&fd_dq, &fd)) {
+               sdqcr_complete = 1;
+               wake_up(&waitqueue);
+       }
+       return qman_cb_dqrr_consume;
+}
+
+static void cb_ern(struct qman_portal *p, struct qman_fq *fq,
+                  const union qm_mr_entry *msg)
+{
+       pr_crit("cb_ern() unimplemented");
+       WARN_ON(1);
+}
+
+static void cb_fqs(struct qman_portal *p, struct qman_fq *fq,
+                  const union qm_mr_entry *msg)
+{
+       u8 verb = (msg->verb & QM_MR_VERB_TYPE_MASK);
+
+       if ((verb != QM_MR_VERB_FQRN) && (verb != QM_MR_VERB_FQRNI)) {
+               pr_crit("unexpected FQS message");
+               WARN_ON(1);
+               return;
+       }
+       pr_info("Retirement message received\n");
+       retire_complete = 1;
+       wake_up(&waitqueue);
+}
diff --git a/drivers/soc/fsl/qbman/qman_test_stash.c b/drivers/soc/fsl/qbman/qman_test_stash.c
new file mode 100644 (file)
index 0000000..43cf66b
--- /dev/null
@@ -0,0 +1,617 @@
+/* Copyright 2009 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_test.h"
+
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+
+/*
+ * Algorithm:
+ *
+ * Each cpu will have HP_PER_CPU "handlers" set up, each of which incorporates
+ * an rx/tx pair of FQ objects (both of which are stashed on dequeue). The
+ * organisation of FQIDs is such that the HP_PER_CPU*NUM_CPUS handlers will
+ * shuttle a "hot potato" frame around them such that every forwarding action
+ * moves it from one cpu to another. (The use of more than one handler per cpu
+ * is to allow enough handlers/FQs to truly test the significance of caching -
+ * ie. when cache-expiries are occurring.)
+ *
+ * The "hot potato" frame content will be HP_NUM_WORDS*4 bytes in size, and the
+ * first and last words of the frame data will undergo a transformation step on
+ * each forwarding action. To achieve this, each handler will be assigned a
+ * 32-bit "mixer", that is produced using a 32-bit LFSR. When a frame is
+ * received by a handler, the mixer of the expected sender is XOR'd into all
+ * words of the entire frame, which is then validated against the original
+ * values. Then, before forwarding, the entire frame is XOR'd with the mixer of
+ * the current handler. Apart from validating that the frame is taking the
+ * expected path, this also provides some quasi-realistic overheads to each
+ * forwarding action - dereferencing *all* the frame data, computation, and
+ * conditional branching. There is a "special" handler designated to act as the
+ * instigator of the test by creating an enqueuing the "hot potato" frame, and
+ * to determine when the test has completed by counting HP_LOOPS iterations.
+ *
+ * Init phases:
+ *
+ * 1. prepare each cpu's 'hp_cpu' struct using on_each_cpu(,,1) and link them
+ *    into 'hp_cpu_list'. Specifically, set processor_id, allocate HP_PER_CPU
+ *    handlers and link-list them (but do no other handler setup).
+ *
+ * 2. scan over 'hp_cpu_list' HP_PER_CPU times, the first time sets each
+ *    hp_cpu's 'iterator' to point to its first handler. With each loop,
+ *    allocate rx/tx FQIDs and mixer values to the hp_cpu's iterator handler
+ *    and advance the iterator for the next loop. This includes a final fixup,
+ *    which connects the last handler to the first (and which is why phase 2
+ *    and 3 are separate).
+ *
+ * 3. scan over 'hp_cpu_list' HP_PER_CPU times, the first time sets each
+ *    hp_cpu's 'iterator' to point to its first handler. With each loop,
+ *    initialise FQ objects and advance the iterator for the next loop.
+ *    Moreover, do this initialisation on the cpu it applies to so that Rx FQ
+ *    initialisation targets the correct cpu.
+ */
+
+/*
+ * helper to run something on all cpus (can't use on_each_cpu(), as that invokes
+ * the fn from irq context, which is too restrictive).
+ */
+struct bstrap {
+       int (*fn)(void);
+       atomic_t started;
+};
+static int bstrap_fn(void *bs)
+{
+       struct bstrap *bstrap = bs;
+       int err;
+
+       atomic_inc(&bstrap->started);
+       err = bstrap->fn();
+       if (err)
+               return err;
+       while (!kthread_should_stop())
+               msleep(20);
+       return 0;
+}
+static int on_all_cpus(int (*fn)(void))
+{
+       int cpu;
+
+       for_each_cpu(cpu, cpu_online_mask) {
+               struct bstrap bstrap = {
+                       .fn = fn,
+                       .started = ATOMIC_INIT(0)
+               };
+               struct task_struct *k = kthread_create(bstrap_fn, &bstrap,
+                       "hotpotato%d", cpu);
+               int ret;
+
+               if (IS_ERR(k))
+                       return -ENOMEM;
+               kthread_bind(k, cpu);
+               wake_up_process(k);
+               /*
+                * If we call kthread_stop() before the "wake up" has had an
+                * effect, then the thread may exit with -EINTR without ever
+                * running the function. So poll until it's started before
+                * requesting it to stop.
+                */
+               while (!atomic_read(&bstrap.started))
+                       msleep(20);
+               ret = kthread_stop(k);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+struct hp_handler {
+
+       /* The following data is stashed when 'rx' is dequeued; */
+       /* -------------- */
+       /* The Rx FQ, dequeues of which will stash the entire hp_handler */
+       struct qman_fq rx;
+       /* The Tx FQ we should forward to */
+       struct qman_fq tx;
+       /* The value we XOR post-dequeue, prior to validating */
+       u32 rx_mixer;
+       /* The value we XOR pre-enqueue, after validating */
+       u32 tx_mixer;
+       /* what the hotpotato address should be on dequeue */
+       dma_addr_t addr;
+       u32 *frame_ptr;
+
+       /* The following data isn't (necessarily) stashed on dequeue; */
+       /* -------------- */
+       u32 fqid_rx, fqid_tx;
+       /* list node for linking us into 'hp_cpu' */
+       struct list_head node;
+       /* Just to check ... */
+       unsigned int processor_id;
+} ____cacheline_aligned;
+
+struct hp_cpu {
+       /* identify the cpu we run on; */
+       unsigned int processor_id;
+       /* root node for the per-cpu list of handlers */
+       struct list_head handlers;
+       /* list node for linking us into 'hp_cpu_list' */
+       struct list_head node;
+       /*
+        * when repeatedly scanning 'hp_list', each time linking the n'th
+        * handlers together, this is used as per-cpu iterator state
+        */
+       struct hp_handler *iterator;
+};
+
+/* Each cpu has one of these */
+static DEFINE_PER_CPU(struct hp_cpu, hp_cpus);
+
+/* links together the hp_cpu structs, in first-come first-serve order. */
+static LIST_HEAD(hp_cpu_list);
+static spinlock_t hp_lock = __SPIN_LOCK_UNLOCKED(hp_lock);
+
+static unsigned int hp_cpu_list_length;
+
+/* the "special" handler, that starts and terminates the test. */
+static struct hp_handler *special_handler;
+static int loop_counter;
+
+/* handlers are allocated out of this, so they're properly aligned. */
+static struct kmem_cache *hp_handler_slab;
+
+/* this is the frame data */
+static void *__frame_ptr;
+static u32 *frame_ptr;
+static dma_addr_t frame_dma;
+
+/* the main function waits on this */
+static DECLARE_WAIT_QUEUE_HEAD(queue);
+
+#define HP_PER_CPU     2
+#define HP_LOOPS       8
+/* 80 bytes, like a small ethernet frame, and bleeds into a second cacheline */
+#define HP_NUM_WORDS   80
+/* First word of the LFSR-based frame data */
+#define HP_FIRST_WORD  0xabbaf00d
+
+static inline u32 do_lfsr(u32 prev)
+{
+       return (prev >> 1) ^ (-(prev & 1u) & 0xd0000001u);
+}
+
+static int allocate_frame_data(void)
+{
+       u32 lfsr = HP_FIRST_WORD;
+       int loop;
+       struct platform_device *pdev = platform_device_alloc("foobar", -1);
+
+       if (!pdev) {
+               pr_crit("platform_device_alloc() failed");
+               return -EIO;
+       }
+       if (platform_device_add(pdev)) {
+               pr_crit("platform_device_add() failed");
+               return -EIO;
+       }
+       __frame_ptr = kmalloc(4 * HP_NUM_WORDS, GFP_KERNEL);
+       if (!__frame_ptr)
+               return -ENOMEM;
+
+       frame_ptr = PTR_ALIGN(__frame_ptr, 64);
+       for (loop = 0; loop < HP_NUM_WORDS; loop++) {
+               frame_ptr[loop] = lfsr;
+               lfsr = do_lfsr(lfsr);
+       }
+       frame_dma = dma_map_single(&pdev->dev, frame_ptr, 4 * HP_NUM_WORDS,
+                                  DMA_BIDIRECTIONAL);
+       platform_device_del(pdev);
+       platform_device_put(pdev);
+       return 0;
+}
+
+static void deallocate_frame_data(void)
+{
+       kfree(__frame_ptr);
+}
+
+static inline int process_frame_data(struct hp_handler *handler,
+                                    const struct qm_fd *fd)
+{
+       u32 *p = handler->frame_ptr;
+       u32 lfsr = HP_FIRST_WORD;
+       int loop;
+
+       if (qm_fd_addr_get64(fd) != handler->addr) {
+               pr_crit("bad frame address");
+               return -EIO;
+       }
+       for (loop = 0; loop < HP_NUM_WORDS; loop++, p++) {
+               *p ^= handler->rx_mixer;
+               if (*p != lfsr) {
+                       pr_crit("corrupt frame data");
+                       return -EIO;
+               }
+               *p ^= handler->tx_mixer;
+               lfsr = do_lfsr(lfsr);
+       }
+       return 0;
+}
+
+static enum qman_cb_dqrr_result normal_dqrr(struct qman_portal *portal,
+                                           struct qman_fq *fq,
+                                           const struct qm_dqrr_entry *dqrr)
+{
+       struct hp_handler *handler = (struct hp_handler *)fq;
+
+       if (process_frame_data(handler, &dqrr->fd)) {
+               WARN_ON(1);
+               goto skip;
+       }
+       if (qman_enqueue(&handler->tx, &dqrr->fd)) {
+               pr_crit("qman_enqueue() failed");
+               WARN_ON(1);
+       }
+skip:
+       return qman_cb_dqrr_consume;
+}
+
+static enum qman_cb_dqrr_result special_dqrr(struct qman_portal *portal,
+                                            struct qman_fq *fq,
+                                            const struct qm_dqrr_entry *dqrr)
+{
+       struct hp_handler *handler = (struct hp_handler *)fq;
+
+       process_frame_data(handler, &dqrr->fd);
+       if (++loop_counter < HP_LOOPS) {
+               if (qman_enqueue(&handler->tx, &dqrr->fd)) {
+                       pr_crit("qman_enqueue() failed");
+                       WARN_ON(1);
+                       goto skip;
+               }
+       } else {
+               pr_info("Received final (%dth) frame\n", loop_counter);
+               wake_up(&queue);
+       }
+skip:
+       return qman_cb_dqrr_consume;
+}
+
+static int create_per_cpu_handlers(void)
+{
+       struct hp_handler *handler;
+       int loop;
+       struct hp_cpu *hp_cpu = this_cpu_ptr(&hp_cpus);
+
+       hp_cpu->processor_id = smp_processor_id();
+       spin_lock(&hp_lock);
+       list_add_tail(&hp_cpu->node, &hp_cpu_list);
+       hp_cpu_list_length++;
+       spin_unlock(&hp_lock);
+       INIT_LIST_HEAD(&hp_cpu->handlers);
+       for (loop = 0; loop < HP_PER_CPU; loop++) {
+               handler = kmem_cache_alloc(hp_handler_slab, GFP_KERNEL);
+               if (!handler) {
+                       pr_crit("kmem_cache_alloc() failed");
+                       WARN_ON(1);
+                       return -EIO;
+               }
+               handler->processor_id = hp_cpu->processor_id;
+               handler->addr = frame_dma;
+               handler->frame_ptr = frame_ptr;
+               list_add_tail(&handler->node, &hp_cpu->handlers);
+       }
+       return 0;
+}
+
+static int destroy_per_cpu_handlers(void)
+{
+       struct list_head *loop, *tmp;
+       struct hp_cpu *hp_cpu = this_cpu_ptr(&hp_cpus);
+
+       spin_lock(&hp_lock);
+       list_del(&hp_cpu->node);
+       spin_unlock(&hp_lock);
+       list_for_each_safe(loop, tmp, &hp_cpu->handlers) {
+               u32 flags = 0;
+               struct hp_handler *handler = list_entry(loop, struct hp_handler,
+                                                       node);
+               if (qman_retire_fq(&handler->rx, &flags) ||
+                   (flags & QMAN_FQ_STATE_BLOCKOOS)) {
+                       pr_crit("qman_retire_fq(rx) failed, flags: %x", flags);
+                       WARN_ON(1);
+                       return -EIO;
+               }
+               if (qman_oos_fq(&handler->rx)) {
+                       pr_crit("qman_oos_fq(rx) failed");
+                       WARN_ON(1);
+                       return -EIO;
+               }
+               qman_destroy_fq(&handler->rx);
+               qman_destroy_fq(&handler->tx);
+               qman_release_fqid(handler->fqid_rx);
+               list_del(&handler->node);
+               kmem_cache_free(hp_handler_slab, handler);
+       }
+       return 0;
+}
+
+static inline u8 num_cachelines(u32 offset)
+{
+       u8 res = (offset + (L1_CACHE_BYTES - 1))
+                        / (L1_CACHE_BYTES);
+       if (res > 3)
+               return 3;
+       return res;
+}
+#define STASH_DATA_CL \
+       num_cachelines(HP_NUM_WORDS * 4)
+#define STASH_CTX_CL \
+       num_cachelines(offsetof(struct hp_handler, fqid_rx))
+
+static int init_handler(void *h)
+{
+       struct qm_mcc_initfq opts;
+       struct hp_handler *handler = h;
+       int err;
+
+       if (handler->processor_id != smp_processor_id()) {
+               err = -EIO;
+               goto failed;
+       }
+       /* Set up rx */
+       memset(&handler->rx, 0, sizeof(handler->rx));
+       if (handler == special_handler)
+               handler->rx.cb.dqrr = special_dqrr;
+       else
+               handler->rx.cb.dqrr = normal_dqrr;
+       err = qman_create_fq(handler->fqid_rx, 0, &handler->rx);
+       if (err) {
+               pr_crit("qman_create_fq(rx) failed");
+               goto failed;
+       }
+       memset(&opts, 0, sizeof(opts));
+       opts.we_mask = QM_INITFQ_WE_FQCTRL | QM_INITFQ_WE_CONTEXTA;
+       opts.fqd.fq_ctrl = QM_FQCTRL_CTXASTASHING;
+       qm_fqd_set_stashing(&opts.fqd, 0, STASH_DATA_CL, STASH_CTX_CL);
+       err = qman_init_fq(&handler->rx, QMAN_INITFQ_FLAG_SCHED |
+                          QMAN_INITFQ_FLAG_LOCAL, &opts);
+       if (err) {
+               pr_crit("qman_init_fq(rx) failed");
+               goto failed;
+       }
+       /* Set up tx */
+       memset(&handler->tx, 0, sizeof(handler->tx));
+       err = qman_create_fq(handler->fqid_tx, QMAN_FQ_FLAG_NO_MODIFY,
+                            &handler->tx);
+       if (err) {
+               pr_crit("qman_create_fq(tx) failed");
+               goto failed;
+       }
+
+       return 0;
+failed:
+       return err;
+}
+
+static void init_handler_cb(void *h)
+{
+       if (init_handler(h))
+               WARN_ON(1);
+}
+
+static int init_phase2(void)
+{
+       int loop;
+       u32 fqid = 0;
+       u32 lfsr = 0xdeadbeef;
+       struct hp_cpu *hp_cpu;
+       struct hp_handler *handler;
+
+       for (loop = 0; loop < HP_PER_CPU; loop++) {
+               list_for_each_entry(hp_cpu, &hp_cpu_list, node) {
+                       int err;
+
+                       if (!loop)
+                               hp_cpu->iterator = list_first_entry(
+                                               &hp_cpu->handlers,
+                                               struct hp_handler, node);
+                       else
+                               hp_cpu->iterator = list_entry(
+                                               hp_cpu->iterator->node.next,
+                                               struct hp_handler, node);
+                       /* Rx FQID is the previous handler's Tx FQID */
+                       hp_cpu->iterator->fqid_rx = fqid;
+                       /* Allocate new FQID for Tx */
+                       err = qman_alloc_fqid(&fqid);
+                       if (err) {
+                               pr_crit("qman_alloc_fqid() failed");
+                               return err;
+                       }
+                       hp_cpu->iterator->fqid_tx = fqid;
+                       /* Rx mixer is the previous handler's Tx mixer */
+                       hp_cpu->iterator->rx_mixer = lfsr;
+                       /* Get new mixer for Tx */
+                       lfsr = do_lfsr(lfsr);
+                       hp_cpu->iterator->tx_mixer = lfsr;
+               }
+       }
+       /* Fix up the first handler (fqid_rx==0, rx_mixer=0xdeadbeef) */
+       hp_cpu = list_first_entry(&hp_cpu_list, struct hp_cpu, node);
+       handler = list_first_entry(&hp_cpu->handlers, struct hp_handler, node);
+       if (handler->fqid_rx != 0 || handler->rx_mixer != 0xdeadbeef)
+               return 1;
+       handler->fqid_rx = fqid;
+       handler->rx_mixer = lfsr;
+       /* and tag it as our "special" handler */
+       special_handler = handler;
+       return 0;
+}
+
+static int init_phase3(void)
+{
+       int loop, err;
+       struct hp_cpu *hp_cpu;
+
+       for (loop = 0; loop < HP_PER_CPU; loop++) {
+               list_for_each_entry(hp_cpu, &hp_cpu_list, node) {
+                       if (!loop)
+                               hp_cpu->iterator = list_first_entry(
+                                               &hp_cpu->handlers,
+                                               struct hp_handler, node);
+                       else
+                               hp_cpu->iterator = list_entry(
+                                               hp_cpu->iterator->node.next,
+                                               struct hp_handler, node);
+                       preempt_disable();
+                       if (hp_cpu->processor_id == smp_processor_id()) {
+                               err = init_handler(hp_cpu->iterator);
+                               if (err)
+                                       return err;
+                       } else {
+                               smp_call_function_single(hp_cpu->processor_id,
+                                       init_handler_cb, hp_cpu->iterator, 1);
+                       }
+                       preempt_enable();
+               }
+       }
+       return 0;
+}
+
+static int send_first_frame(void *ignore)
+{
+       u32 *p = special_handler->frame_ptr;
+       u32 lfsr = HP_FIRST_WORD;
+       int loop, err;
+       struct qm_fd fd;
+
+       if (special_handler->processor_id != smp_processor_id()) {
+               err = -EIO;
+               goto failed;
+       }
+       memset(&fd, 0, sizeof(fd));
+       qm_fd_addr_set64(&fd, special_handler->addr);
+       qm_fd_set_contig_big(&fd, HP_NUM_WORDS * 4);
+       for (loop = 0; loop < HP_NUM_WORDS; loop++, p++) {
+               if (*p != lfsr) {
+                       err = -EIO;
+                       pr_crit("corrupt frame data");
+                       goto failed;
+               }
+               *p ^= special_handler->tx_mixer;
+               lfsr = do_lfsr(lfsr);
+       }
+       pr_info("Sending first frame\n");
+       err = qman_enqueue(&special_handler->tx, &fd);
+       if (err) {
+               pr_crit("qman_enqueue() failed");
+               goto failed;
+       }
+
+       return 0;
+failed:
+       return err;
+}
+
+static void send_first_frame_cb(void *ignore)
+{
+       if (send_first_frame(NULL))
+               WARN_ON(1);
+}
+
+int qman_test_stash(void)
+{
+       int err;
+
+       if (cpumask_weight(cpu_online_mask) < 2) {
+               pr_info("%s(): skip - only 1 CPU\n", __func__);
+               return 0;
+       }
+
+       pr_info("%s(): Starting\n", __func__);
+
+       hp_cpu_list_length = 0;
+       loop_counter = 0;
+       hp_handler_slab = kmem_cache_create("hp_handler_slab",
+                       sizeof(struct hp_handler), L1_CACHE_BYTES,
+                       SLAB_HWCACHE_ALIGN, NULL);
+       if (!hp_handler_slab) {
+               err = -EIO;
+               pr_crit("kmem_cache_create() failed");
+               goto failed;
+       }
+
+       err = allocate_frame_data();
+       if (err)
+               goto failed;
+
+       /* Init phase 1 */
+       pr_info("Creating %d handlers per cpu...\n", HP_PER_CPU);
+       if (on_all_cpus(create_per_cpu_handlers)) {
+               err = -EIO;
+               pr_crit("on_each_cpu() failed");
+               goto failed;
+       }
+       pr_info("Number of cpus: %d, total of %d handlers\n",
+               hp_cpu_list_length, hp_cpu_list_length * HP_PER_CPU);
+
+       err = init_phase2();
+       if (err)
+               goto failed;
+
+       err = init_phase3();
+       if (err)
+               goto failed;
+
+       preempt_disable();
+       if (special_handler->processor_id == smp_processor_id()) {
+               err = send_first_frame(NULL);
+               if (err)
+                       goto failed;
+       } else {
+               smp_call_function_single(special_handler->processor_id,
+                                        send_first_frame_cb, NULL, 1);
+       }
+       preempt_enable();
+
+       wait_event(queue, loop_counter == HP_LOOPS);
+       deallocate_frame_data();
+       if (on_all_cpus(destroy_per_cpu_handlers)) {
+               err = -EIO;
+               pr_crit("on_each_cpu() failed");
+               goto failed;
+       }
+       kmem_cache_destroy(hp_handler_slab);
+       pr_info("%s(): Finished\n", __func__);
+
+       return 0;
+failed:
+       WARN_ON(1);
+       return err;
+}
index 333eb2215a5795c65d505b4ae81a3f09b511a3c2..0aaf429f31d571f569fe0916b52d10cf3dadfd49 100644 (file)
@@ -41,7 +41,8 @@ struct qe_gpio_chip {
 
 static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc)
 {
-       struct qe_gpio_chip *qe_gc = gpiochip_get_data(&mm_gc->gc);
+       struct qe_gpio_chip *qe_gc =
+               container_of(mm_gc, struct qe_gpio_chip, mm_gc);
        struct qe_pio_regs __iomem *regs = mm_gc->regs;
 
        qe_gc->cpdata = in_be32(&regs->cpdata);
index 7026507e6f1d5e4e53245a43c7c5839d5a4f9171..2707a827261b10378ef45460082463cf80890dd1 100644 (file)
@@ -69,8 +69,8 @@ static phys_addr_t qebase = -1;
 phys_addr_t get_qe_base(void)
 {
        struct device_node *qe;
-       int size;
-       const u32 *prop;
+       int ret;
+       struct resource res;
 
        if (qebase != -1)
                return qebase;
@@ -82,9 +82,9 @@ phys_addr_t get_qe_base(void)
                        return qebase;
        }
 
-       prop = of_get_property(qe, "reg", &size);
-       if (prop && size >= sizeof(*prop))
-               qebase = of_translate_address(qe, prop);
+       ret = of_address_to_resource(qe, 0, &res);
+       if (!ret)
+               qebase = res.start;
        of_node_put(qe);
 
        return qebase;
index 41eff805a9041c879627b96395a584e545960b5f..104e68d9b84f281c96e77b3861e5f11f1348cc49 100644 (file)
@@ -70,6 +70,11 @@ int cpm_muram_init(void)
        }
 
        muram_pool = gen_pool_create(0, -1);
+       if (!muram_pool) {
+               pr_err("Cannot allocate memory pool for CPM/QE muram");
+               ret = -ENOMEM;
+               goto out_muram;
+       }
        muram_pbase = of_translate_address(np, zero);
        if (muram_pbase == (phys_addr_t)OF_BAD_ADDR) {
                pr_err("Cannot translate zero through CPM muram node");
@@ -116,6 +121,9 @@ static unsigned long cpm_muram_alloc_common(unsigned long size,
        struct muram_block *entry;
        unsigned long start;
 
+       if (!muram_pool && cpm_muram_init())
+               goto out2;
+
        start = gen_pool_alloc_algo(muram_pool, size, algo, data);
        if (!start)
                goto out2;
index 5e48b147017866dca9b98a1d2703d374a0d9c60b..a1048b44e6b93309499ddaac4bdb77d8b747c773 100644 (file)
@@ -99,7 +99,7 @@ int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm,
        utdm->tdm_port = val;
        ut_info->uf_info.tdm_num = utdm->tdm_port;
 
-       if (of_get_property(np, "fsl,tdm-internal-loopback", NULL))
+       if (of_property_read_bool(np, "fsl,tdm-internal-loopback"))
                utdm->tdm_mode = TDM_INTERNAL_LOOPBACK;
        else
                utdm->tdm_mode = TDM_NORMAL;
@@ -167,7 +167,7 @@ int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm,
        }
 
        if (siram_init_flag == 0) {
-               memset_io(utdm->siram, 0,  res->end - res->start + 1);
+               memset_io(utdm->siram, 0,  resource_size(res));
                siram_init_flag = 1;
        }
 
index c29040fdf9a7757b7c0d428616e244a6e4d5f697..1091b9f1dd070e3d27c269402b43b0a09d96bcdc 100644 (file)
@@ -423,8 +423,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
                actual_pages = get_user_pages(task, task->mm,
                                          (unsigned long)buf & ~(PAGE_SIZE - 1),
                                          num_pages,
-                                         (type == PAGELIST_READ) /*Write */ ,
-                                         0 /*Force */ ,
+                                         (type == PAGELIST_READ) ? FOLL_WRITE : 0,
                                          pages,
                                          NULL /*vmas */);
                up_read(&task->mm->mmap_sem);
index e11c0e07471bc7ebba04e48d3efc746236ec2f4e..7b6cd4d80621e38ff6d47fcd87b45fbe9cd4259b 100644 (file)
@@ -1477,8 +1477,7 @@ dump_phys_mem(void *virt_addr, uint32_t num_bytes)
                current->mm,              /* mm */
                (unsigned long)virt_addr, /* start */
                num_pages,                /* len */
-               0,                        /* write */
-               0,                        /* force */
+               0,                        /* gup_flags */
                pages,                    /* pages (array of page pointers) */
                NULL);                    /* vmas */
        up_read(&current->mm->mmap_sem);
index 2d702ca6556f5b6185a95ec007ada7b124226272..a13541bdc726899807dd8723c92c21312fbc6439 100644 (file)
@@ -186,7 +186,7 @@ config HISI_THERMAL
 
 config IMX_THERMAL
        tristate "Temperature sensor driver for Freescale i.MX SoCs"
-       depends on CPU_THERMAL
+       depends on (ARCH_MXC && CPU_THERMAL) || COMPILE_TEST
        depends on MFD_SYSCON
        depends on OF
        help
@@ -195,6 +195,26 @@ config IMX_THERMAL
          cpufreq is used as the cooling device to throttle CPUs when the
          passive trip is crossed.
 
+config MAX77620_THERMAL
+       tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
+       depends on MFD_MAX77620
+       depends on OF
+       help
+         Support for die junction temperature warning alarm for Maxim
+         Semiconductor PMIC MAX77620 device. Device generates two alarm
+         interrupts when PMIC die temperature cross the threshold of
+         120 degC and 140 degC.
+
+config QORIQ_THERMAL
+       tristate "QorIQ Thermal Monitoring Unit"
+       depends on THERMAL_OF
+       depends on HAS_IOMEM
+       help
+         Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
+         It supports one critical trip point and one passive trip point. The
+         cpufreq is used as the cooling device to throttle CPUs when the
+         passive trip is crossed.
+
 config SPEAR_THERMAL
        tristate "SPEAr thermal sensor driver"
        depends on PLAT_SPEAR || COMPILE_TEST
@@ -332,6 +352,16 @@ menu "ACPI INT340X thermal drivers"
 source drivers/thermal/int340x_thermal/Kconfig
 endmenu
 
+config INTEL_BXT_PMIC_THERMAL
+       tristate "Intel Broxton PMIC thermal driver"
+       depends on X86 && INTEL_SOC_PMIC && REGMAP
+       help
+         Select this driver for Intel Broxton PMIC with ADC channels monitoring
+         system temperature measurements and alerts.
+         This driver is used for monitoring the ADC channels of PMIC and handles
+         the alert trip point interrupts and notifies the thermal framework with
+         the trip point and temperature details of the zone.
+
 config INTEL_PCH_THERMAL
        tristate "Intel PCH Thermal Reporting Driver"
        depends on X86 && PCI
@@ -399,4 +429,9 @@ config GENERIC_ADC_THERMAL
          to this driver. This driver reports the temperature by reading ADC
          channel and converts it to temperature based on lookup table.
 
+menu "Qualcomm thermal drivers"
+depends on (ARCH_QCOM && OF) || COMPILE_TEST
+source "drivers/thermal/qcom/Kconfig"
+endmenu
+
 endif
index 10b07c14f8a9f75363323d0c69542606d8dfed96..c92eb22a41ff89f3f1c61c61977de7eb9ba516ae 100644 (file)
@@ -37,6 +37,8 @@ obj-$(CONFIG_DB8500_THERMAL)  += db8500_thermal.o
 obj-$(CONFIG_ARMADA_THERMAL)   += armada_thermal.o
 obj-$(CONFIG_TANGO_THERMAL)    += tango_thermal.o
 obj-$(CONFIG_IMX_THERMAL)      += imx_thermal.o
+obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
+obj-$(CONFIG_QORIQ_THERMAL)    += qoriq_thermal.o
 obj-$(CONFIG_DB8500_CPUFREQ_COOLING)   += db8500_cpufreq_cooling.o
 obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
 obj-$(CONFIG_X86_PKG_TEMP_THERMAL)     += x86_pkg_temp_thermal.o
@@ -45,8 +47,10 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL)  += intel_soc_dts_thermal.o
 obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL)  += intel_quark_dts_thermal.o
 obj-$(CONFIG_TI_SOC_THERMAL)   += ti-soc-thermal/
 obj-$(CONFIG_INT340X_THERMAL)  += int340x_thermal/
+obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
 obj-$(CONFIG_INTEL_PCH_THERMAL)        += intel_pch_thermal.o
 obj-$(CONFIG_ST_THERMAL)       += st/
+obj-$(CONFIG_QCOM_TSENS)       += qcom/
 obj-$(CONFIG_TEGRA_SOCTHERM)   += tegra/
 obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
 obj-$(CONFIG_MTK_THERMAL)      += mtk_thermal.o
index a32b41783b7778b7d32cd8fc44026fb8476ace0d..9ce0e9eef9230c725eb510692f47e794ece255e9 100644 (file)
@@ -74,7 +74,7 @@ struct power_table {
  *     cpufreq frequencies.
  * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
  * @node: list_head to link all cpufreq_cooling_device together.
- * @last_load: load measured by the latest call to cpufreq_get_actual_power()
+ * @last_load: load measured by the latest call to cpufreq_get_requested_power()
  * @time_in_idle: previous reading of the absolute time that this cpu was idle
  * @time_in_idle_timestamp: wall time of the last invocation of
  *     get_cpu_idle_time_us()
index 652acd8fbe48e2334ab887df96272859f12c11bf..e776cea80cfcb19bc61c12908b71969a55f3e64e 100644 (file)
@@ -306,7 +306,7 @@ static void db8500_thermal_work(struct work_struct *work)
        if (cur_mode == THERMAL_DEVICE_DISABLED)
                return;
 
-       thermal_zone_device_update(pzone->therm_dev);
+       thermal_zone_device_update(pzone->therm_dev, THERMAL_EVENT_UNSPECIFIED);
        dev_dbg(&pzone->therm_dev->device, "thermal work finished.\n");
 }
 
index 01f0015f80dc3f4d03409aff8d4732c1f17e57b6..81631b110e178f3b186746f32c8a8f4c001c0046 100644 (file)
@@ -312,7 +312,7 @@ static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev,
        unsigned long freq;
        u32 static_power;
 
-       if (state < 0 || state >= dfc->freq_table_size)
+       if (state >= dfc->freq_table_size)
                return -EINVAL;
 
        freq = dfc->freq_table[state];
index bb118a152cbbde3c570f90fb5355bc04afe54beb..fc5e5057f0de73bbf5567bc5e14a9a824414a189 100644 (file)
@@ -65,7 +65,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
                if (instance->target == 0 && tz->temperature >= trip_temp)
                        instance->target = 1;
                else if (instance->target == 1 &&
-                               tz->temperature < trip_temp - trip_hyst)
+                               tz->temperature <= trip_temp - trip_hyst)
                        instance->target = 0;
 
                dev_dbg(&instance->cdev->device, "target=%d\n",
index 97fad8f51e1c891ec26a296e25a6059419e529a0..f6429666a1cfe03d1f122266938a5343cf744054 100644 (file)
@@ -237,7 +237,8 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
                if (!data->sensors[i].tzd)
                        continue;
 
-               thermal_zone_device_update(data->sensors[i].tzd);
+               thermal_zone_device_update(data->sensors[i].tzd,
+                                          THERMAL_EVENT_UNSPECIFIED);
        }
 
        return IRQ_HANDLED;
index e473548b5d289d7dda47bdd8c423209fc1ab245f..06912f0602b75ba1a7d801babc686f5305838b59 100644 (file)
@@ -246,7 +246,7 @@ static int imx_set_mode(struct thermal_zone_device *tz,
        }
 
        data->mode = mode;
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return 0;
 }
@@ -457,7 +457,7 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev)
        dev_dbg(&data->tz->device, "THERMAL ALARM: T > %d\n",
                data->alarm_temp / 1000);
 
-       thermal_zone_device_update(data->tz);
+       thermal_zone_device_update(data->tz, THERMAL_EVENT_UNSPECIFIED);
 
        return IRQ_HANDLED;
 }
index 69df3d960303170070d3a35f2037602129fbb613..8e90b3151a42d36248cd4f90b73ef2fb73370c3d 100644 (file)
@@ -35,7 +35,8 @@ static void int3402_notify(acpi_handle handle, u32 event, void *data)
        case INT3402_PERF_CHANGED_EVENT:
                break;
        case INT3402_THERMAL_EVENT:
-               int340x_thermal_zone_device_update(priv->int340x_zone);
+               int340x_thermal_zone_device_update(priv->int340x_zone,
+                                                  THERMAL_TRIP_VIOLATED);
                break;
        default:
                break;
index 50a7a08e3a15ee1e351953bd5a9b8308d8c9602f..c4890c9437ebaaa6ca7562a94a6c37a400c9f92d 100644 (file)
@@ -25,6 +25,7 @@
 #define INT3403_TYPE_CHARGER           0x0B
 #define INT3403_TYPE_BATTERY           0x0C
 #define INT3403_PERF_CHANGED_EVENT     0x80
+#define INT3403_PERF_TRIP_POINT_CHANGED        0x81
 #define INT3403_THERMAL_EVENT          0x90
 
 /* Preserved structure for future expandbility */
@@ -72,7 +73,13 @@ static void int3403_notify(acpi_handle handle,
        case INT3403_PERF_CHANGED_EVENT:
                break;
        case INT3403_THERMAL_EVENT:
-               int340x_thermal_zone_device_update(obj->int340x_zone);
+               int340x_thermal_zone_device_update(obj->int340x_zone,
+                                                  THERMAL_TRIP_VIOLATED);
+               break;
+       case INT3403_PERF_TRIP_POINT_CHANGED:
+               int340x_thermal_read_trips(obj->int340x_zone);
+               int340x_thermal_zone_device_update(obj->int340x_zone,
+                                                  THERMAL_TRIP_CHANGED);
                break;
        default:
                dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
index b9b2666aa94c93e39b1c7bd6e23c07dfe4245890..145a5c53ff5c0fd16c579352124394ece156c0eb 100644 (file)
@@ -177,6 +177,42 @@ static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
        return 0;
 }
 
+int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
+{
+       int trip_cnt = int34x_zone->aux_trip_nr;
+       int i;
+
+       int34x_zone->crt_trip_id = -1;
+       if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
+                                            &int34x_zone->crt_temp))
+               int34x_zone->crt_trip_id = trip_cnt++;
+
+       int34x_zone->hot_trip_id = -1;
+       if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT",
+                                            &int34x_zone->hot_temp))
+               int34x_zone->hot_trip_id = trip_cnt++;
+
+       int34x_zone->psv_trip_id = -1;
+       if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV",
+                                            &int34x_zone->psv_temp))
+               int34x_zone->psv_trip_id = trip_cnt++;
+
+       for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
+               char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
+
+               if (int340x_thermal_get_trip_config(int34x_zone->adev->handle,
+                                       name,
+                                       &int34x_zone->act_trips[i].temp))
+                       break;
+
+               int34x_zone->act_trips[i].id = trip_cnt++;
+               int34x_zone->act_trips[i].valid = true;
+       }
+
+       return trip_cnt;
+}
+EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
+
 static struct thermal_zone_params int340x_thermal_params = {
        .governor_name = "user_space",
        .no_hwmon = true,
@@ -188,7 +224,7 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
        struct int34x_thermal_zone *int34x_thermal_zone;
        acpi_status status;
        unsigned long long trip_cnt;
-       int trip_mask = 0, i;
+       int trip_mask = 0;
        int ret;
 
        int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
@@ -214,28 +250,8 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
                int34x_thermal_zone->aux_trip_nr = trip_cnt;
        }
 
-       int34x_thermal_zone->crt_trip_id = -1;
-       if (!int340x_thermal_get_trip_config(adev->handle, "_CRT",
-                                            &int34x_thermal_zone->crt_temp))
-               int34x_thermal_zone->crt_trip_id = trip_cnt++;
-       int34x_thermal_zone->hot_trip_id = -1;
-       if (!int340x_thermal_get_trip_config(adev->handle, "_HOT",
-                                            &int34x_thermal_zone->hot_temp))
-               int34x_thermal_zone->hot_trip_id = trip_cnt++;
-       int34x_thermal_zone->psv_trip_id = -1;
-       if (!int340x_thermal_get_trip_config(adev->handle, "_PSV",
-                                            &int34x_thermal_zone->psv_temp))
-               int34x_thermal_zone->psv_trip_id = trip_cnt++;
-       for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
-               char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
+       trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone);
 
-               if (int340x_thermal_get_trip_config(adev->handle, name,
-                               &int34x_thermal_zone->act_trips[i].temp))
-                       break;
-
-               int34x_thermal_zone->act_trips[i].id = trip_cnt++;
-               int34x_thermal_zone->act_trips[i].valid = true;
-       }
        int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
                                                                adev->handle);
 
index aaadf724ff2ef858807715ae395b2aef9c0b740e..5f3ba4775c5cdd49945bf006b05cdb263d2cc4ac 100644 (file)
@@ -46,6 +46,7 @@ struct int34x_thermal_zone {
 struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *,
                                struct thermal_zone_device_ops *override_ops);
 void int340x_thermal_zone_remove(struct int34x_thermal_zone *);
+int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone);
 
 static inline void int340x_thermal_zone_set_priv_data(
                        struct int34x_thermal_zone *tzone, void *priv_data)
@@ -60,9 +61,10 @@ static inline void *int340x_thermal_zone_get_priv_data(
 }
 
 static inline void int340x_thermal_zone_device_update(
-                       struct int34x_thermal_zone *tzone)
+                                       struct int34x_thermal_zone *tzone,
+                                       enum thermal_notify_event event)
 {
-       thermal_zone_device_update(tzone->zone);
+       thermal_zone_device_update(tzone->zone, event);
 }
 
 #endif
index 42c1ac057bad85c3f3237ea5cc09bccc1aad7010..ff3b36f339e34fb0a2cbc261d9db0ccda6a9b528 100644 (file)
@@ -258,7 +258,8 @@ static void proc_thermal_notify(acpi_handle handle, u32 event, void *data)
        switch (event) {
        case PROC_POWER_CAPABILITY_CHANGED:
                proc_thermal_read_ppcc(proc_priv);
-               int340x_thermal_zone_device_update(proc_priv->int340x_zone);
+               int340x_thermal_zone_device_update(proc_priv->int340x_zone,
+                               THERMAL_DEVICE_POWER_CAPABILITY_CHANGED);
                break;
        default:
                dev_err(proc_priv->dev, "Unsupported event [0x%x]\n", event);
diff --git a/drivers/thermal/intel_bxt_pmic_thermal.c b/drivers/thermal/intel_bxt_pmic_thermal.c
new file mode 100644 (file)
index 0000000..0f19a39
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Intel Broxton PMIC thermal driver
+ *
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/mfd/intel_soc_pmic.h>
+
+#define BXTWC_THRM0IRQ         0x4E04
+#define BXTWC_THRM1IRQ         0x4E05
+#define BXTWC_THRM2IRQ         0x4E06
+#define BXTWC_MTHRM0IRQ                0x4E12
+#define BXTWC_MTHRM1IRQ                0x4E13
+#define BXTWC_MTHRM2IRQ                0x4E14
+#define BXTWC_STHRM0IRQ                0x4F19
+#define BXTWC_STHRM1IRQ                0x4F1A
+#define BXTWC_STHRM2IRQ                0x4F1B
+
+struct trip_config_map {
+       u16 irq_reg;
+       u16 irq_en;
+       u16 evt_stat;
+       u8 irq_mask;
+       u8 irq_en_mask;
+       u8 evt_mask;
+       u8 trip_num;
+};
+
+struct thermal_irq_map {
+       char handle[20];
+       int num_trips;
+       const struct trip_config_map *trip_config;
+};
+
+struct pmic_thermal_data {
+       const struct thermal_irq_map *maps;
+       int num_maps;
+};
+
+static const struct trip_config_map bxtwc_str0_trip_config[] = {
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x01,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x01,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x01,
+               .trip_num = 0
+       },
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x10,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x10,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x10,
+               .trip_num = 1
+       }
+};
+
+static const struct trip_config_map bxtwc_str1_trip_config[] = {
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x02,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x02,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x02,
+               .trip_num = 0
+       },
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x20,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x20,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x20,
+               .trip_num = 1
+       },
+};
+
+static const struct trip_config_map bxtwc_str2_trip_config[] = {
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x04,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x04,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x04,
+               .trip_num = 0
+       },
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x40,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x40,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x40,
+               .trip_num = 1
+       },
+};
+
+static const struct trip_config_map bxtwc_str3_trip_config[] = {
+       {
+               .irq_reg = BXTWC_THRM2IRQ,
+               .irq_mask = 0x10,
+               .irq_en = BXTWC_MTHRM2IRQ,
+               .irq_en_mask = 0x10,
+               .evt_stat = BXTWC_STHRM2IRQ,
+               .evt_mask = 0x10,
+               .trip_num = 0
+       },
+};
+
+static const struct thermal_irq_map bxtwc_thermal_irq_map[] = {
+       {
+               .handle = "STR0",
+               .trip_config = bxtwc_str0_trip_config,
+               .num_trips = ARRAY_SIZE(bxtwc_str0_trip_config),
+       },
+       {
+               .handle = "STR1",
+               .trip_config = bxtwc_str1_trip_config,
+               .num_trips = ARRAY_SIZE(bxtwc_str1_trip_config),
+       },
+       {
+               .handle = "STR2",
+               .trip_config = bxtwc_str2_trip_config,
+               .num_trips = ARRAY_SIZE(bxtwc_str2_trip_config),
+       },
+       {
+               .handle = "STR3",
+               .trip_config = bxtwc_str3_trip_config,
+               .num_trips = ARRAY_SIZE(bxtwc_str3_trip_config),
+       },
+};
+
+static const struct pmic_thermal_data bxtwc_thermal_data = {
+       .maps = bxtwc_thermal_irq_map,
+       .num_maps = ARRAY_SIZE(bxtwc_thermal_irq_map),
+};
+
+static irqreturn_t pmic_thermal_irq_handler(int irq, void *data)
+{
+       struct platform_device *pdev = data;
+       struct thermal_zone_device *tzd;
+       struct pmic_thermal_data *td;
+       struct intel_soc_pmic *pmic;
+       struct regmap *regmap;
+       u8 reg_val, mask, irq_stat, trip;
+       u16 reg, evt_stat_reg;
+       int i, j, ret;
+
+       pmic = dev_get_drvdata(pdev->dev.parent);
+       regmap = pmic->regmap;
+       td = (struct pmic_thermal_data *)
+               platform_get_device_id(pdev)->driver_data;
+
+       /* Resolve thermal irqs */
+       for (i = 0; i < td->num_maps; i++) {
+               for (j = 0; j < td->maps[i].num_trips; j++) {
+                       reg = td->maps[i].trip_config[j].irq_reg;
+                       mask = td->maps[i].trip_config[j].irq_mask;
+                       /*
+                        * Read the irq register to resolve whether the
+                        * interrupt was triggered for this sensor
+                        */
+                       if (regmap_read(regmap, reg, &ret))
+                               return IRQ_HANDLED;
+
+                       reg_val = (u8)ret;
+                       irq_stat = ((u8)ret & mask);
+
+                       if (!irq_stat)
+                               continue;
+
+                       /*
+                        * Read the status register to find out what
+                        * event occurred i.e a high or a low
+                        */
+                       evt_stat_reg = td->maps[i].trip_config[j].evt_stat;
+                       if (regmap_read(regmap, evt_stat_reg, &ret))
+                               return IRQ_HANDLED;
+
+                       trip = td->maps[i].trip_config[j].trip_num;
+                       tzd = thermal_zone_get_zone_by_name(td->maps[i].handle);
+                       if (!IS_ERR(tzd))
+                               thermal_zone_device_update(tzd,
+                                               THERMAL_EVENT_UNSPECIFIED);
+
+                       /* Clear the appropriate irq */
+                       regmap_write(regmap, reg, reg_val & mask);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int pmic_thermal_probe(struct platform_device *pdev)
+{
+       struct regmap_irq_chip_data *regmap_irq_chip;
+       struct pmic_thermal_data *thermal_data;
+       int ret, irq, virq, i, j, pmic_irq_count;
+       struct intel_soc_pmic *pmic;
+       struct regmap *regmap;
+       struct device *dev;
+       u16 reg;
+       u8 mask;
+
+       dev = &pdev->dev;
+       pmic = dev_get_drvdata(pdev->dev.parent);
+       if (!pmic) {
+               dev_err(dev, "Failed to get struct intel_soc_pmic pointer\n");
+               return -ENODEV;
+       }
+
+       thermal_data = (struct pmic_thermal_data *)
+                               platform_get_device_id(pdev)->driver_data;
+       if (!thermal_data) {
+               dev_err(dev, "No thermal data initialized!!\n");
+               return -ENODEV;
+       }
+
+       regmap = pmic->regmap;
+       regmap_irq_chip = pmic->irq_chip_data_level2;
+
+       pmic_irq_count = 0;
+       while ((irq = platform_get_irq(pdev, pmic_irq_count)) != -ENXIO) {
+               virq = regmap_irq_get_virq(regmap_irq_chip, irq);
+               if (virq < 0) {
+                       dev_err(dev, "failed to get virq by irq %d\n", irq);
+                       return virq;
+               }
+
+               ret = devm_request_threaded_irq(&pdev->dev, virq,
+                               NULL, pmic_thermal_irq_handler,
+                               IRQF_ONESHOT, "pmic_thermal", pdev);
+
+               if (ret) {
+                       dev_err(dev, "request irq(%d) failed: %d\n", virq, ret);
+                       return ret;
+               }
+               pmic_irq_count++;
+       }
+
+       /* Enable thermal interrupts */
+       for (i = 0; i < thermal_data->num_maps; i++) {
+               for (j = 0; j < thermal_data->maps[i].num_trips; j++) {
+                       reg = thermal_data->maps[i].trip_config[j].irq_en;
+                       mask = thermal_data->maps[i].trip_config[j].irq_en_mask;
+                       ret = regmap_update_bits(regmap, reg, mask, 0x00);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static const struct platform_device_id pmic_thermal_id_table[] = {
+       {
+               .name = "bxt_wcove_thermal",
+               .driver_data = (kernel_ulong_t)&bxtwc_thermal_data,
+       },
+       {},
+};
+
+static struct platform_driver pmic_thermal_driver = {
+       .probe = pmic_thermal_probe,
+       .driver = {
+               .name = "pmic_thermal",
+       },
+       .id_table = pmic_thermal_id_table,
+};
+
+MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table);
+module_platform_driver(pmic_thermal_driver);
+
+MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>");
+MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver");
+MODULE_LICENSE("GPL v2");
index f72e1db3216f6f446458467964c9108986ea8c4f..e0813dfaa2783c17ea43012df3cedf2f70b11f3f 100644 (file)
@@ -391,7 +391,8 @@ void intel_soc_dts_iosf_interrupt_handler(struct intel_soc_dts_sensors *sensors)
 
                for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
                        pr_debug("TZD update for zone %d\n", i);
-                       thermal_zone_device_update(sensors->soc_dts[i].tzone);
+                       thermal_zone_device_update(sensors->soc_dts[i].tzone,
+                                                  THERMAL_EVENT_UNSPECIFIED);
                }
        } else
                spin_unlock_irqrestore(&sensors->intr_notify_lock, flags);
diff --git a/drivers/thermal/max77620_thermal.c b/drivers/thermal/max77620_thermal.c
new file mode 100644 (file)
index 0000000..83905ff
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Junction temperature thermal driver for Maxim Max77620.
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *        Mallikarjun Kasoju <mkasoju@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max77620.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#define MAX77620_NORMAL_OPERATING_TEMP 100000
+#define MAX77620_TJALARM1_TEMP         120000
+#define MAX77620_TJALARM2_TEMP         140000
+
+struct max77620_therm_info {
+       struct device                   *dev;
+       struct regmap                   *rmap;
+       struct thermal_zone_device      *tz_device;
+       int                             irq_tjalarm1;
+       int                             irq_tjalarm2;
+};
+
+/**
+ * max77620_thermal_read_temp: Read PMIC die temperatue.
+ * @data:      Device specific data.
+ * temp:       Temperature in millidegrees Celsius
+ *
+ * The actual temperature of PMIC die is not available from PMIC.
+ * PMIC only tells the status if it has crossed or not the threshold level
+ * of 120degC or 140degC.
+ * If threshold has not been crossed then assume die temperature as 100degC
+ * else 120degC or 140deG based on the PMIC die temp threshold status.
+ *
+ * Return 0 on success otherwise error number to show reason of failure.
+ */
+
+static int max77620_thermal_read_temp(void *data, int *temp)
+{
+       struct max77620_therm_info *mtherm = data;
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(mtherm->rmap, MAX77620_REG_STATLBT, &val);
+       if (ret < 0) {
+               dev_err(mtherm->dev, "Failed to read STATLBT: %d\n", ret);
+               return ret;
+       }
+
+       if (val & MAX77620_IRQ_TJALRM2_MASK)
+               *temp = MAX77620_TJALARM2_TEMP;
+       else if (val & MAX77620_IRQ_TJALRM1_MASK)
+               *temp = MAX77620_TJALARM1_TEMP;
+       else
+               *temp = MAX77620_NORMAL_OPERATING_TEMP;
+
+       return 0;
+}
+
+static const struct thermal_zone_of_device_ops max77620_thermal_ops = {
+       .get_temp = max77620_thermal_read_temp,
+};
+
+static irqreturn_t max77620_thermal_irq(int irq, void *data)
+{
+       struct max77620_therm_info *mtherm = data;
+
+       if (irq == mtherm->irq_tjalarm1)
+               dev_warn(mtherm->dev, "Junction Temp Alarm1(120C) occurred\n");
+       else if (irq == mtherm->irq_tjalarm2)
+               dev_crit(mtherm->dev, "Junction Temp Alarm2(140C) occurred\n");
+
+       thermal_zone_device_update(mtherm->tz_device,
+                                  THERMAL_EVENT_UNSPECIFIED);
+
+       return IRQ_HANDLED;
+}
+
+static int max77620_thermal_probe(struct platform_device *pdev)
+{
+       struct max77620_therm_info *mtherm;
+       int ret;
+
+       mtherm = devm_kzalloc(&pdev->dev, sizeof(*mtherm), GFP_KERNEL);
+       if (!mtherm)
+               return -ENOMEM;
+
+       mtherm->irq_tjalarm1 = platform_get_irq(pdev, 0);
+       mtherm->irq_tjalarm2 = platform_get_irq(pdev, 1);
+       if ((mtherm->irq_tjalarm1 < 0) || (mtherm->irq_tjalarm2 < 0)) {
+               dev_err(&pdev->dev, "Alarm irq number not available\n");
+               return -EINVAL;
+       }
+
+       pdev->dev.of_node = pdev->dev.parent->of_node;
+
+       mtherm->dev = &pdev->dev;
+       mtherm->rmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!mtherm->rmap) {
+               dev_err(&pdev->dev, "Failed to get parent regmap\n");
+               return -ENODEV;
+       }
+
+       mtherm->tz_device = devm_thermal_zone_of_sensor_register(&pdev->dev, 0,
+                               mtherm, &max77620_thermal_ops);
+       if (IS_ERR(mtherm->tz_device)) {
+               ret = PTR_ERR(mtherm->tz_device);
+               dev_err(&pdev->dev, "Failed to register thermal zone: %d\n",
+                       ret);
+               return ret;
+       }
+
+       ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm1, NULL,
+                                       max77620_thermal_irq,
+                                       IRQF_ONESHOT | IRQF_SHARED,
+                                       dev_name(&pdev->dev), mtherm);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to request irq1: %d\n", ret);
+               return ret;
+       }
+
+       ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm2, NULL,
+                                       max77620_thermal_irq,
+                                       IRQF_ONESHOT | IRQF_SHARED,
+                                       dev_name(&pdev->dev), mtherm);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to request irq2: %d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, mtherm);
+
+       return 0;
+}
+
+static struct platform_device_id max77620_thermal_devtype[] = {
+       { .name = "max77620-thermal", },
+       {},
+};
+
+static struct platform_driver max77620_thermal_driver = {
+       .driver = {
+               .name = "max77620-thermal",
+       },
+       .probe = max77620_thermal_probe,
+       .id_table = max77620_thermal_devtype,
+};
+
+module_platform_driver(max77620_thermal_driver);
+
+MODULE_DESCRIPTION("Max77620 Junction temperature Thermal driver");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_AUTHOR("Mallikarjun Kasoju <mkasoju@nvidia.com>");
+MODULE_LICENSE("GPL v2");
index 262ab0a2266f71242708cca87e524ba2bbf179f1..34169c32d4956ffe8bb146ab0ae66a8f4ebbdbf2 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (c) 2015 MediaTek Inc.
  * Author: Hanyi Wu <hanyi.wu@mediatek.com>
  *         Sascha Hauer <s.hauer@pengutronix.de>
+ *         Dawei Chien <dawei.chien@mediatek.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
@@ -21,6 +22,7 @@
 #include <linux/nvmem-consumer.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/io.h>
@@ -88,6 +90,7 @@
 #define TEMP_ADCVALIDMASK_VALID_HIGH           BIT(5)
 #define TEMP_ADCVALIDMASK_VALID_POS(bit)       (bit)
 
+/* MT8173 thermal sensors */
 #define MT8173_TS1     0
 #define MT8173_TS2     1
 #define MT8173_TS3     2
 /* The number of sensing points per bank */
 #define MT8173_NUM_SENSORS_PER_ZONE    4
 
-/* Layout of the fuses providing the calibration data */
+/*
+ * Layout of the fuses providing the calibration data
+ * These macros could be used for both MT8173 and MT2701.
+ * MT8173 has five sensors and need five VTS calibration data,
+ * and MT2701 has three sensors and need three VTS calibration data.
+ */
 #define MT8173_CALIB_BUF0_VALID                BIT(0)
 #define MT8173_CALIB_BUF1_ADC_GE(x)    (((x) >> 22) & 0x3ff)
 #define MT8173_CALIB_BUF0_VTS_TS1(x)   (((x) >> 17) & 0x1ff)
 #define MT8173_CALIB_BUF0_DEGC_CALI(x) (((x) >> 1) & 0x3f)
 #define MT8173_CALIB_BUF0_O_SLOPE(x)   (((x) >> 26) & 0x3f)
 
+/* MT2701 thermal sensors */
+#define MT2701_TS1     0
+#define MT2701_TS2     1
+#define MT2701_TSABB   2
+
+/* AUXADC channel 11 is used for the temperature sensors */
+#define MT2701_TEMP_AUXADC_CHANNEL     11
+
+/* The total number of temperature sensors in the MT2701 */
+#define MT2701_NUM_SENSORS     3
+
 #define THERMAL_NAME    "mtk-thermal"
 
+/* The number of sensing points per bank */
+#define MT2701_NUM_SENSORS_PER_ZONE    3
+
 struct mtk_thermal;
 
+struct thermal_bank_cfg {
+       unsigned int num_sensors;
+       const int *sensors;
+};
+
 struct mtk_thermal_bank {
        struct mtk_thermal *mt;
        int id;
 };
 
+struct mtk_thermal_data {
+       s32 num_banks;
+       s32 num_sensors;
+       s32 auxadc_channel;
+       const int *sensor_mux_values;
+       const int *msr;
+       const int *adcpnp;
+       struct thermal_bank_cfg bank_data[];
+};
+
 struct mtk_thermal {
        struct device *dev;
        void __iomem *thermal_base;
 
        struct clk *clk_peri_therm;
        struct clk *clk_auxadc;
-
-       struct mtk_thermal_bank banks[MT8173_NUM_ZONES];
-
        /* lock: for getting and putting banks */
        struct mutex lock;
 
@@ -144,16 +178,44 @@ struct mtk_thermal {
        s32 o_slope;
        s32 vts[MT8173_NUM_SENSORS];
 
+       const struct mtk_thermal_data *conf;
+       struct mtk_thermal_bank banks[];
 };
 
-struct mtk_thermal_bank_cfg {
-       unsigned int num_sensors;
-       unsigned int sensors[MT8173_NUM_SENSORS_PER_ZONE];
+/* MT8173 thermal sensor data */
+const int mt8173_bank_data[MT8173_NUM_ZONES][3] = {
+       { MT8173_TS2, MT8173_TS3 },
+       { MT8173_TS2, MT8173_TS4 },
+       { MT8173_TS1, MT8173_TS2, MT8173_TSABB },
+       { MT8173_TS2 },
 };
 
-static const int sensor_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 };
+const int mt8173_msr[MT8173_NUM_SENSORS_PER_ZONE] = {
+       TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR2
+};
 
-/*
+const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = {
+       TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3
+};
+
+const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 };
+
+/* MT2701 thermal sensor data */
+const int mt2701_bank_data[MT2701_NUM_SENSORS] = {
+       MT2701_TS1, MT2701_TS2, MT2701_TSABB
+};
+
+const int mt2701_msr[MT2701_NUM_SENSORS_PER_ZONE] = {
+       TEMP_MSR0, TEMP_MSR1, TEMP_MSR2
+};
+
+const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = {
+       TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2
+};
+
+const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 };
+
+/**
  * The MT8173 thermal controller has four banks. Each bank can read up to
  * four temperature sensors simultaneously. The MT8173 has a total of 5
  * temperature sensors. We use each bank to measure a certain area of the
@@ -166,42 +228,53 @@ static const int sensor_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 };
  * data, and this indeed needs the temperatures of the individual banks
  * for making better decisions.
  */
-static const struct mtk_thermal_bank_cfg bank_data[] = {
-       {
-               .num_sensors = 2,
-               .sensors = { MT8173_TS2, MT8173_TS3 },
-       }, {
-               .num_sensors = 2,
-               .sensors = { MT8173_TS2, MT8173_TS4 },
-       }, {
-               .num_sensors = 3,
-               .sensors = { MT8173_TS1, MT8173_TS2, MT8173_TSABB },
-       }, {
-               .num_sensors = 1,
-               .sensors = { MT8173_TS2 },
+static const struct mtk_thermal_data mt8173_thermal_data = {
+       .auxadc_channel = MT8173_TEMP_AUXADC_CHANNEL,
+       .num_banks = MT8173_NUM_ZONES,
+       .num_sensors = MT8173_NUM_SENSORS,
+       .bank_data = {
+               {
+                       .num_sensors = 2,
+                       .sensors = mt8173_bank_data[0],
+               }, {
+                       .num_sensors = 2,
+                       .sensors = mt8173_bank_data[1],
+               }, {
+                       .num_sensors = 3,
+                       .sensors = mt8173_bank_data[2],
+               }, {
+                       .num_sensors = 1,
+                       .sensors = mt8173_bank_data[3],
+               },
        },
+       .msr = mt8173_msr,
+       .adcpnp = mt8173_adcpnp,
+       .sensor_mux_values = mt8173_mux_values,
 };
 
-struct mtk_thermal_sense_point {
-       int msr;
-       int adcpnp;
-};
-
-static const struct mtk_thermal_sense_point
-               sensing_points[MT8173_NUM_SENSORS_PER_ZONE] = {
-       {
-               .msr = TEMP_MSR0,
-               .adcpnp = TEMP_ADCPNP0,
-       }, {
-               .msr = TEMP_MSR1,
-               .adcpnp = TEMP_ADCPNP1,
-       }, {
-               .msr = TEMP_MSR2,
-               .adcpnp = TEMP_ADCPNP2,
-       }, {
-               .msr = TEMP_MSR3,
-               .adcpnp = TEMP_ADCPNP3,
+/**
+ * The MT2701 thermal controller has one bank, which can read up to
+ * three temperature sensors simultaneously. The MT2701 has a total of 3
+ * temperature sensors.
+ *
+ * The thermal core only gets the maximum temperature of this one bank,
+ * so the bank concept wouldn't be necessary here. However, the SVS (Smart
+ * Voltage Scaling) unit makes its decisions based on the same bank
+ * data.
+ */
+static const struct mtk_thermal_data mt2701_thermal_data = {
+       .auxadc_channel = MT2701_TEMP_AUXADC_CHANNEL,
+       .num_banks = 1,
+       .num_sensors = MT2701_NUM_SENSORS,
+       .bank_data = {
+               {
+                       .num_sensors = 3,
+                       .sensors = mt2701_bank_data,
+               },
        },
+       .msr = mt2701_msr,
+       .adcpnp = mt2701_adcpnp,
+       .sensor_mux_values = mt2701_mux_values,
 };
 
 /**
@@ -270,13 +343,16 @@ static void mtk_thermal_put_bank(struct mtk_thermal_bank *bank)
 static int mtk_thermal_bank_temperature(struct mtk_thermal_bank *bank)
 {
        struct mtk_thermal *mt = bank->mt;
+       const struct mtk_thermal_data *conf = mt->conf;
        int i, temp = INT_MIN, max = INT_MIN;
        u32 raw;
 
-       for (i = 0; i < bank_data[bank->id].num_sensors; i++) {
-               raw = readl(mt->thermal_base + sensing_points[i].msr);
+       for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) {
+               raw = readl(mt->thermal_base + conf->msr[i]);
 
-               temp = raw_to_mcelsius(mt, bank_data[bank->id].sensors[i], raw);
+               temp = raw_to_mcelsius(mt,
+                                      conf->bank_data[bank->id].sensors[i],
+                                      raw);
 
                /*
                 * The first read of a sensor often contains very high bogus
@@ -299,7 +375,7 @@ static int mtk_read_temp(void *data, int *temperature)
        int i;
        int tempmax = INT_MIN;
 
-       for (i = 0; i < MT8173_NUM_ZONES; i++) {
+       for (i = 0; i < mt->conf->num_banks; i++) {
                struct mtk_thermal_bank *bank = &mt->banks[i];
 
                mtk_thermal_get_bank(bank);
@@ -322,7 +398,7 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
                                  u32 apmixed_phys_base, u32 auxadc_phys_base)
 {
        struct mtk_thermal_bank *bank = &mt->banks[num];
-       const struct mtk_thermal_bank_cfg *cfg = &bank_data[num];
+       const struct mtk_thermal_data *conf = mt->conf;
        int i;
 
        bank->id = num;
@@ -368,7 +444,7 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
         * this value will be stored to TEMP_PNPMUXADDR (TEMP_SPARE0)
         * automatically by hw
         */
-       writel(BIT(MT8173_TEMP_AUXADC_CHANNEL), mt->thermal_base + TEMP_ADCMUX);
+       writel(BIT(conf->auxadc_channel), mt->thermal_base + TEMP_ADCMUX);
 
        /* AHB address for auxadc mux selection */
        writel(auxadc_phys_base + AUXADC_CON1_CLR_V,
@@ -379,18 +455,18 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
               mt->thermal_base + TEMP_PNPMUXADDR);
 
        /* AHB value for auxadc enable */
-       writel(BIT(MT8173_TEMP_AUXADC_CHANNEL), mt->thermal_base + TEMP_ADCEN);
+       writel(BIT(conf->auxadc_channel), mt->thermal_base + TEMP_ADCEN);
 
        /* AHB address for auxadc enable (channel 0 immediate mode selected) */
        writel(auxadc_phys_base + AUXADC_CON1_SET_V,
               mt->thermal_base + TEMP_ADCENADDR);
 
        /* AHB address for auxadc valid bit */
-       writel(auxadc_phys_base + AUXADC_DATA(MT8173_TEMP_AUXADC_CHANNEL),
+       writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel),
               mt->thermal_base + TEMP_ADCVALIDADDR);
 
        /* AHB address for auxadc voltage output */
-       writel(auxadc_phys_base + AUXADC_DATA(MT8173_TEMP_AUXADC_CHANNEL),
+       writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel),
               mt->thermal_base + TEMP_ADCVOLTADDR);
 
        /* read valid & voltage are at the same register */
@@ -407,11 +483,12 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
        writel(TEMP_ADCWRITECTRL_ADC_MUX_WRITE,
               mt->thermal_base + TEMP_ADCWRITECTRL);
 
-       for (i = 0; i < cfg->num_sensors; i++)
-               writel(sensor_mux_values[cfg->sensors[i]],
-                      mt->thermal_base + sensing_points[i].adcpnp);
+       for (i = 0; i < conf->bank_data[num].num_sensors; i++)
+               writel(conf->sensor_mux_values[conf->bank_data[num].sensors[i]],
+                      mt->thermal_base + conf->adcpnp[i]);
 
-       writel((1 << cfg->num_sensors) - 1, mt->thermal_base + TEMP_MONCTL0);
+       writel((1 << conf->bank_data[num].num_sensors) - 1,
+              mt->thermal_base + TEMP_MONCTL0);
 
        writel(TEMP_ADCWRITECTRL_ADC_PNP_WRITE |
               TEMP_ADCWRITECTRL_ADC_MUX_WRITE,
@@ -442,7 +519,7 @@ static int mtk_thermal_get_calibration_data(struct device *dev,
 
        /* Start with default values */
        mt->adc_ge = 512;
-       for (i = 0; i < MT8173_NUM_SENSORS; i++)
+       for (i = 0; i < mt->conf->num_sensors; i++)
                mt->vts[i] = 260;
        mt->degc_cali = 40;
        mt->o_slope = 0;
@@ -486,18 +563,37 @@ out:
        return ret;
 }
 
+static const struct of_device_id mtk_thermal_of_match[] = {
+       {
+               .compatible = "mediatek,mt8173-thermal",
+               .data = (void *)&mt8173_thermal_data,
+       },
+       {
+               .compatible = "mediatek,mt2701-thermal",
+               .data = (void *)&mt2701_thermal_data,
+       }, {
+       },
+};
+MODULE_DEVICE_TABLE(of, mtk_thermal_of_match);
+
 static int mtk_thermal_probe(struct platform_device *pdev)
 {
        int ret, i;
        struct device_node *auxadc, *apmixedsys, *np = pdev->dev.of_node;
        struct mtk_thermal *mt;
        struct resource *res;
+       const struct of_device_id *of_id;
        u64 auxadc_phys_base, apmixed_phys_base;
+       struct thermal_zone_device *tzdev;
 
        mt = devm_kzalloc(&pdev->dev, sizeof(*mt), GFP_KERNEL);
        if (!mt)
                return -ENOMEM;
 
+       of_id = of_match_device(mtk_thermal_of_match, &pdev->dev);
+       if (of_id)
+               mt->conf = (const struct mtk_thermal_data *)of_id->data;
+
        mt->clk_peri_therm = devm_clk_get(&pdev->dev, "therm");
        if (IS_ERR(mt->clk_peri_therm))
                return PTR_ERR(mt->clk_peri_therm);
@@ -565,17 +661,23 @@ static int mtk_thermal_probe(struct platform_device *pdev)
                goto err_disable_clk_auxadc;
        }
 
-       for (i = 0; i < MT8173_NUM_ZONES; i++)
+       for (i = 0; i < mt->conf->num_banks; i++)
                mtk_thermal_init_bank(mt, i, apmixed_phys_base,
                                      auxadc_phys_base);
 
        platform_set_drvdata(pdev, mt);
 
-       devm_thermal_zone_of_sensor_register(&pdev->dev, 0, mt,
-                                            &mtk_thermal_ops);
+       tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, mt,
+                                                    &mtk_thermal_ops);
+       if (IS_ERR(tzdev)) {
+               ret = PTR_ERR(tzdev);
+               goto err_disable_clk_peri_therm;
+       }
 
        return 0;
 
+err_disable_clk_peri_therm:
+       clk_disable_unprepare(mt->clk_peri_therm);
 err_disable_clk_auxadc:
        clk_disable_unprepare(mt->clk_auxadc);
 
@@ -592,13 +694,6 @@ static int mtk_thermal_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id mtk_thermal_of_match[] = {
-       {
-               .compatible = "mediatek,mt8173-thermal",
-       }, {
-       },
-};
-
 static struct platform_driver mtk_thermal_driver = {
        .probe = mtk_thermal_probe,
        .remove = mtk_thermal_remove,
@@ -610,6 +705,7 @@ static struct platform_driver mtk_thermal_driver = {
 
 module_platform_driver(mtk_thermal_driver);
 
+MODULE_AUTHOR("Dawei Chien <dawei.chien@mediatek.com>");
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 MODULE_AUTHOR("Hanyi Wu <hanyi.wu@mediatek.com>");
 MODULE_DESCRIPTION("Mediatek thermal driver");
index b8e509c60848e4f94dc621185d334b59f8151c1a..d04ec3b9e5ff21f68aa4a25d4a9d3e4caef0f4ec 100644 (file)
@@ -101,6 +101,17 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz,
        return data->ops->get_temp(data->sensor_data, temp);
 }
 
+static int of_thermal_set_trips(struct thermal_zone_device *tz,
+                               int low, int high)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (!data->ops || !data->ops->set_trips)
+               return -EINVAL;
+
+       return data->ops->set_trips(data->sensor_data, low, high);
+}
+
 /**
  * of_thermal_get_ntrips - function to export number of available trip
  *                        points.
@@ -181,9 +192,6 @@ static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
 {
        struct __thermal_zone *data = tz->devdata;
 
-       if (!data->ops || !data->ops->set_emul_temp)
-               return -EINVAL;
-
        return data->ops->set_emul_temp(data->sensor_data, temp);
 }
 
@@ -191,25 +199,11 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
                                enum thermal_trend *trend)
 {
        struct __thermal_zone *data = tz->devdata;
-       long dev_trend;
-       int r;
 
        if (!data->ops->get_trend)
                return -EINVAL;
 
-       r = data->ops->get_trend(data->sensor_data, &dev_trend);
-       if (r)
-               return r;
-
-       /* TODO: These intervals might have some thresholds, but in core code */
-       if (dev_trend > 0)
-               *trend = THERMAL_TREND_RAISING;
-       else if (dev_trend < 0)
-               *trend = THERMAL_TREND_DROPPING;
-       else
-               *trend = THERMAL_TREND_STABLE;
-
-       return 0;
+       return data->ops->get_trend(data->sensor_data, trip, trend);
 }
 
 static int of_thermal_bind(struct thermal_zone_device *thermal,
@@ -292,7 +286,7 @@ static int of_thermal_set_mode(struct thermal_zone_device *tz,
        mutex_unlock(&tz->lock);
 
        data->mode = mode;
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return 0;
 }
@@ -427,7 +421,17 @@ thermal_zone_of_add_sensor(struct device_node *zone,
 
        tzd->ops->get_temp = of_thermal_get_temp;
        tzd->ops->get_trend = of_thermal_get_trend;
-       tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
+
+       /*
+        * The thermal zone core will calculate the window if they have set the
+        * optional set_trips pointer.
+        */
+       if (ops->set_trips)
+               tzd->ops->set_trips = of_thermal_set_trips;
+
+       if (ops->set_emul_temp)
+               tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
+
        mutex_unlock(&tzd->lock);
 
        return tzd;
@@ -596,7 +600,7 @@ static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
  * Return: On success returns a valid struct thermal_zone_device,
  * otherwise, it returns a corresponding ERR_PTR(). Caller must
  * check the return value with help of IS_ERR() helper.
- * Registered hermal_zone_device device will automatically be
+ * Registered thermal_zone_device device will automatically be
  * released when device is unbounded.
  */
 struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
index f8a3c60bef94bcf32ca388ab0c2de8de034dab7c..819c6d5d7aa714914b6611fb306fd74bea48344c 100644 (file)
@@ -150,7 +150,7 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data)
 {
        struct qpnp_tm_chip *chip = data;
 
-       thermal_zone_device_update(chip->tz_dev);
+       thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED);
 
        return IRQ_HANDLED;
 }
diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig
new file mode 100644 (file)
index 0000000..be32e5a
--- /dev/null
@@ -0,0 +1,11 @@
+config QCOM_TSENS
+       tristate "Qualcomm TSENS Temperature Alarm"
+       depends on THERMAL
+       depends on QCOM_QFPROM
+       depends on ARCH_QCOM || COMPILE_TEST
+       help
+         This enables the thermal sysfs driver for the TSENS device. It shows
+         up in Sysfs as a thermal zone with multiple trip points. Disabling the
+         thermal zone device via the mode file results in disabling the sensor.
+         Also able to set threshold temperature for both hot and cold and update
+         when a threshold is reached.
diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile
new file mode 100644 (file)
index 0000000..2cc2193
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_QCOM_TSENS)       += qcom_tsens.o
+qcom_tsens-y                   += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o
diff --git a/drivers/thermal/qcom/tsens-8916.c b/drivers/thermal/qcom/tsens-8916.c
new file mode 100644 (file)
index 0000000..fdf561b
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include "tsens.h"
+
+/* eeprom layout data for 8916 */
+#define BASE0_MASK     0x0000007f
+#define BASE1_MASK     0xfe000000
+#define BASE0_SHIFT    0
+#define BASE1_SHIFT    25
+
+#define S0_P1_MASK     0x00000f80
+#define S1_P1_MASK     0x003e0000
+#define S2_P1_MASK     0xf8000000
+#define S3_P1_MASK     0x000003e0
+#define S4_P1_MASK     0x000f8000
+
+#define S0_P2_MASK     0x0001f000
+#define S1_P2_MASK     0x07c00000
+#define S2_P2_MASK     0x0000001f
+#define S3_P2_MASK     0x00007c00
+#define S4_P2_MASK     0x01f00000
+
+#define S0_P1_SHIFT    7
+#define S1_P1_SHIFT    17
+#define S2_P1_SHIFT    27
+#define S3_P1_SHIFT    5
+#define S4_P1_SHIFT    15
+
+#define S0_P2_SHIFT    12
+#define S1_P2_SHIFT    22
+#define S2_P2_SHIFT    0
+#define S3_P2_SHIFT    10
+#define S4_P2_SHIFT    20
+
+#define CAL_SEL_MASK   0xe0000000
+#define CAL_SEL_SHIFT  29
+
+static int calibrate_8916(struct tsens_device *tmdev)
+{
+       int base0 = 0, base1 = 0, i;
+       u32 p1[5], p2[5];
+       int mode = 0;
+       u32 *qfprom_cdata, *qfprom_csel;
+
+       qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib");
+       if (IS_ERR(qfprom_cdata))
+               return PTR_ERR(qfprom_cdata);
+
+       qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel");
+       if (IS_ERR(qfprom_csel))
+               return PTR_ERR(qfprom_csel);
+
+       mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT;
+       dev_dbg(tmdev->dev, "calibration mode is %d\n", mode);
+
+       switch (mode) {
+       case TWO_PT_CALIB:
+               base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT;
+               p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT;
+               p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT;
+               p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT;
+               p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT;
+               p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT;
+               for (i = 0; i < tmdev->num_sensors; i++)
+                       p2[i] = ((base1 + p2[i]) << 3);
+               /* Fall through */
+       case ONE_PT_CALIB2:
+               base0 = (qfprom_cdata[0] & BASE0_MASK);
+               p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT;
+               p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT;
+               p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT;
+               p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT;
+               p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT;
+               for (i = 0; i < tmdev->num_sensors; i++)
+                       p1[i] = (((base0) + p1[i]) << 3);
+               break;
+       default:
+               for (i = 0; i < tmdev->num_sensors; i++) {
+                       p1[i] = 500;
+                       p2[i] = 780;
+               }
+               break;
+       }
+
+       compute_intercept_slope(tmdev, p1, p2, mode);
+
+       return 0;
+}
+
+static const struct tsens_ops ops_8916 = {
+       .init           = init_common,
+       .calibrate      = calibrate_8916,
+       .get_temp       = get_temp_common,
+};
+
+const struct tsens_data data_8916 = {
+       .num_sensors    = 5,
+       .ops            = &ops_8916,
+       .hw_ids         = (unsigned int []){0, 1, 2, 4, 5 },
+};
diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
new file mode 100644 (file)
index 0000000..0451277
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+#include "tsens.h"
+
+#define CAL_MDEGC              30000
+
+#define CONFIG_ADDR            0x3640
+#define CONFIG_ADDR_8660       0x3620
+/* CONFIG_ADDR bitmasks */
+#define CONFIG                 0x9b
+#define CONFIG_MASK            0xf
+#define CONFIG_8660            1
+#define CONFIG_SHIFT_8660      28
+#define CONFIG_MASK_8660       (3 << CONFIG_SHIFT_8660)
+
+#define STATUS_CNTL_ADDR_8064  0x3660
+#define CNTL_ADDR              0x3620
+/* CNTL_ADDR bitmasks */
+#define EN                     BIT(0)
+#define SW_RST                 BIT(1)
+#define SENSOR0_EN             BIT(3)
+#define SLP_CLK_ENA            BIT(26)
+#define SLP_CLK_ENA_8660       BIT(24)
+#define MEASURE_PERIOD         1
+#define SENSOR0_SHIFT          3
+
+/* INT_STATUS_ADDR bitmasks */
+#define MIN_STATUS_MASK                BIT(0)
+#define LOWER_STATUS_CLR       BIT(1)
+#define UPPER_STATUS_CLR       BIT(2)
+#define MAX_STATUS_MASK                BIT(3)
+
+#define THRESHOLD_ADDR         0x3624
+/* THRESHOLD_ADDR bitmasks */
+#define THRESHOLD_MAX_LIMIT_SHIFT      24
+#define THRESHOLD_MIN_LIMIT_SHIFT      16
+#define THRESHOLD_UPPER_LIMIT_SHIFT    8
+#define THRESHOLD_LOWER_LIMIT_SHIFT    0
+
+/* Initial temperature threshold values */
+#define LOWER_LIMIT_TH         0x50
+#define UPPER_LIMIT_TH         0xdf
+#define MIN_LIMIT_TH           0x0
+#define MAX_LIMIT_TH           0xff
+
+#define S0_STATUS_ADDR         0x3628
+#define INT_STATUS_ADDR                0x363c
+#define TRDY_MASK              BIT(7)
+#define TIMEOUT_US             100
+
+static int suspend_8960(struct tsens_device *tmdev)
+{
+       int ret;
+       unsigned int mask;
+       struct regmap *map = tmdev->map;
+
+       ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold);
+       if (ret)
+               return ret;
+
+       ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control);
+       if (ret)
+               return ret;
+
+       if (tmdev->num_sensors > 1)
+               mask = SLP_CLK_ENA | EN;
+       else
+               mask = SLP_CLK_ENA_8660 | EN;
+
+       ret = regmap_update_bits(map, CNTL_ADDR, mask, 0);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int resume_8960(struct tsens_device *tmdev)
+{
+       int ret;
+       struct regmap *map = tmdev->map;
+
+       ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
+       if (ret)
+               return ret;
+
+       /*
+        * Separate CONFIG restore is not needed only for 8660 as
+        * config is part of CTRL Addr and its restored as such
+        */
+       if (tmdev->num_sensors > 1) {
+               ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
+               if (ret)
+                       return ret;
+       }
+
+       ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int enable_8960(struct tsens_device *tmdev, int id)
+{
+       int ret;
+       u32 reg, mask;
+
+       ret = regmap_read(tmdev->map, CNTL_ADDR, &reg);
+       if (ret)
+               return ret;
+
+       mask = BIT(id + SENSOR0_SHIFT);
+       ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST);
+       if (ret)
+               return ret;
+
+       if (tmdev->num_sensors > 1)
+               reg |= mask | SLP_CLK_ENA | EN;
+       else
+               reg |= mask | SLP_CLK_ENA_8660 | EN;
+
+       ret = regmap_write(tmdev->map, CNTL_ADDR, reg);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void disable_8960(struct tsens_device *tmdev)
+{
+       int ret;
+       u32 reg_cntl;
+       u32 mask;
+
+       mask = GENMASK(tmdev->num_sensors - 1, 0);
+       mask <<= SENSOR0_SHIFT;
+       mask |= EN;
+
+       ret = regmap_read(tmdev->map, CNTL_ADDR, &reg_cntl);
+       if (ret)
+               return;
+
+       reg_cntl &= ~mask;
+
+       if (tmdev->num_sensors > 1)
+               reg_cntl &= ~SLP_CLK_ENA;
+       else
+               reg_cntl &= ~SLP_CLK_ENA_8660;
+
+       regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+}
+
+static int init_8960(struct tsens_device *tmdev)
+{
+       int ret, i;
+       u32 reg_cntl;
+
+       tmdev->map = dev_get_regmap(tmdev->dev, NULL);
+       if (!tmdev->map)
+               return -ENODEV;
+
+       /*
+        * The status registers for each sensor are discontiguous
+        * because some SoCs have 5 sensors while others have more
+        * but the control registers stay in the same place, i.e
+        * directly after the first 5 status registers.
+        */
+       for (i = 0; i < tmdev->num_sensors; i++) {
+               if (i >= 5)
+                       tmdev->sensor[i].status = S0_STATUS_ADDR + 40;
+               tmdev->sensor[i].status += i * 4;
+       }
+
+       reg_cntl = SW_RST;
+       ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl);
+       if (ret)
+               return ret;
+
+       if (tmdev->num_sensors > 1) {
+               reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
+               reg_cntl &= ~SW_RST;
+               ret = regmap_update_bits(tmdev->map, CONFIG_ADDR,
+                                        CONFIG_MASK, CONFIG);
+       } else {
+               reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16);
+               reg_cntl &= ~CONFIG_MASK_8660;
+               reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660;
+       }
+
+       reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT;
+       ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+       if (ret)
+               return ret;
+
+       reg_cntl |= EN;
+       ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int calibrate_8960(struct tsens_device *tmdev)
+{
+       int i;
+       char *data;
+
+       ssize_t num_read = tmdev->num_sensors;
+       struct tsens_sensor *s = tmdev->sensor;
+
+       data = qfprom_read(tmdev->dev, "calib");
+       if (IS_ERR(data))
+               data = qfprom_read(tmdev->dev, "calib_backup");
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       for (i = 0; i < num_read; i++, s++)
+               s->offset = data[i];
+
+       return 0;
+}
+
+/* Temperature on y axis and ADC-code on x-axis */
+static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
+{
+       int slope, offset;
+
+       slope = thermal_zone_get_slope(s->tzd);
+       offset = CAL_MDEGC - slope * s->offset;
+
+       return adc_code * slope + offset;
+}
+
+static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp)
+{
+       int ret;
+       u32 code, trdy;
+       const struct tsens_sensor *s = &tmdev->sensor[id];
+       unsigned long timeout;
+
+       timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
+       do {
+               ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy);
+               if (ret)
+                       return ret;
+               if (!(trdy & TRDY_MASK))
+                       continue;
+               ret = regmap_read(tmdev->map, s->status, &code);
+               if (ret)
+                       return ret;
+               *temp = code_to_mdegC(code, s);
+               return 0;
+       } while (time_before(jiffies, timeout));
+
+       return -ETIMEDOUT;
+}
+
+static const struct tsens_ops ops_8960 = {
+       .init           = init_8960,
+       .calibrate      = calibrate_8960,
+       .get_temp       = get_temp_8960,
+       .enable         = enable_8960,
+       .disable        = disable_8960,
+       .suspend        = suspend_8960,
+       .resume         = resume_8960,
+};
+
+const struct tsens_data data_8960 = {
+       .num_sensors    = 11,
+       .ops            = &ops_8960,
+};
diff --git a/drivers/thermal/qcom/tsens-8974.c b/drivers/thermal/qcom/tsens-8974.c
new file mode 100644 (file)
index 0000000..9baf77e
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include "tsens.h"
+
+/* eeprom layout data for 8974 */
+#define BASE1_MASK             0xff
+#define S0_P1_MASK             0x3f00
+#define S1_P1_MASK             0xfc000
+#define S2_P1_MASK             0x3f00000
+#define S3_P1_MASK             0xfc000000
+#define S4_P1_MASK             0x3f
+#define S5_P1_MASK             0xfc0
+#define S6_P1_MASK             0x3f000
+#define S7_P1_MASK             0xfc0000
+#define S8_P1_MASK             0x3f000000
+#define S8_P1_MASK_BKP         0x3f
+#define S9_P1_MASK             0x3f
+#define S9_P1_MASK_BKP         0xfc0
+#define S10_P1_MASK            0xfc0
+#define S10_P1_MASK_BKP                0x3f000
+#define CAL_SEL_0_1            0xc0000000
+#define CAL_SEL_2              0x40000000
+#define CAL_SEL_SHIFT          30
+#define CAL_SEL_SHIFT_2                28
+
+#define S0_P1_SHIFT            8
+#define S1_P1_SHIFT            14
+#define S2_P1_SHIFT            20
+#define S3_P1_SHIFT            26
+#define S5_P1_SHIFT            6
+#define S6_P1_SHIFT            12
+#define S7_P1_SHIFT            18
+#define S8_P1_SHIFT            24
+#define S9_P1_BKP_SHIFT                6
+#define S10_P1_SHIFT           6
+#define S10_P1_BKP_SHIFT       12
+
+#define BASE2_SHIFT            12
+#define BASE2_BKP_SHIFT                18
+#define S0_P2_SHIFT            20
+#define S0_P2_BKP_SHIFT                26
+#define S1_P2_SHIFT            26
+#define S2_P2_BKP_SHIFT                6
+#define S3_P2_SHIFT            6
+#define S3_P2_BKP_SHIFT                12
+#define S4_P2_SHIFT            12
+#define S4_P2_BKP_SHIFT                18
+#define S5_P2_SHIFT            18
+#define S5_P2_BKP_SHIFT                24
+#define S6_P2_SHIFT            24
+#define S7_P2_BKP_SHIFT                6
+#define S8_P2_SHIFT            6
+#define S8_P2_BKP_SHIFT                12
+#define S9_P2_SHIFT            12
+#define S9_P2_BKP_SHIFT                18
+#define S10_P2_SHIFT           18
+#define S10_P2_BKP_SHIFT       24
+
+#define BASE2_MASK             0xff000
+#define BASE2_BKP_MASK         0xfc0000
+#define S0_P2_MASK             0x3f00000
+#define S0_P2_BKP_MASK         0xfc000000
+#define S1_P2_MASK             0xfc000000
+#define S1_P2_BKP_MASK         0x3f
+#define S2_P2_MASK             0x3f
+#define S2_P2_BKP_MASK         0xfc0
+#define S3_P2_MASK             0xfc0
+#define S3_P2_BKP_MASK         0x3f000
+#define S4_P2_MASK             0x3f000
+#define S4_P2_BKP_MASK         0xfc0000
+#define S5_P2_MASK             0xfc0000
+#define S5_P2_BKP_MASK         0x3f000000
+#define S6_P2_MASK             0x3f000000
+#define S6_P2_BKP_MASK         0x3f
+#define S7_P2_MASK             0x3f
+#define S7_P2_BKP_MASK         0xfc0
+#define S8_P2_MASK             0xfc0
+#define S8_P2_BKP_MASK         0x3f000
+#define S9_P2_MASK             0x3f000
+#define S9_P2_BKP_MASK         0xfc0000
+#define S10_P2_MASK            0xfc0000
+#define S10_P2_BKP_MASK                0x3f000000
+
+#define BKP_SEL                        0x3
+#define BKP_REDUN_SEL          0xe0000000
+#define BKP_REDUN_SHIFT                29
+
+#define BIT_APPEND             0x3
+
+static int calibrate_8974(struct tsens_device *tmdev)
+{
+       int base1 = 0, base2 = 0, i;
+       u32 p1[11], p2[11];
+       int mode = 0;
+       u32 *calib, *bkp;
+       u32 calib_redun_sel;
+
+       calib = (u32 *)qfprom_read(tmdev->dev, "calib");
+       if (IS_ERR(calib))
+               return PTR_ERR(calib);
+
+       bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup");
+       if (IS_ERR(bkp))
+               return PTR_ERR(bkp);
+
+       calib_redun_sel =  bkp[1] & BKP_REDUN_SEL;
+       calib_redun_sel >>= BKP_REDUN_SHIFT;
+
+       if (calib_redun_sel == BKP_SEL) {
+               mode = (calib[4] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
+               mode |= (calib[5] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
+
+               switch (mode) {
+               case TWO_PT_CALIB:
+                       base2 = (bkp[2] & BASE2_BKP_MASK) >> BASE2_BKP_SHIFT;
+                       p2[0] = (bkp[2] & S0_P2_BKP_MASK) >> S0_P2_BKP_SHIFT;
+                       p2[1] = (bkp[3] & S1_P2_BKP_MASK);
+                       p2[2] = (bkp[3] & S2_P2_BKP_MASK) >> S2_P2_BKP_SHIFT;
+                       p2[3] = (bkp[3] & S3_P2_BKP_MASK) >> S3_P2_BKP_SHIFT;
+                       p2[4] = (bkp[3] & S4_P2_BKP_MASK) >> S4_P2_BKP_SHIFT;
+                       p2[5] = (calib[4] & S5_P2_BKP_MASK) >> S5_P2_BKP_SHIFT;
+                       p2[6] = (calib[5] & S6_P2_BKP_MASK);
+                       p2[7] = (calib[5] & S7_P2_BKP_MASK) >> S7_P2_BKP_SHIFT;
+                       p2[8] = (calib[5] & S8_P2_BKP_MASK) >> S8_P2_BKP_SHIFT;
+                       p2[9] = (calib[5] & S9_P2_BKP_MASK) >> S9_P2_BKP_SHIFT;
+                       p2[10] = (calib[5] & S10_P2_BKP_MASK) >> S10_P2_BKP_SHIFT;
+                       /* Fall through */
+               case ONE_PT_CALIB:
+               case ONE_PT_CALIB2:
+                       base1 = bkp[0] & BASE1_MASK;
+                       p1[0] = (bkp[0] & S0_P1_MASK) >> S0_P1_SHIFT;
+                       p1[1] = (bkp[0] & S1_P1_MASK) >> S1_P1_SHIFT;
+                       p1[2] = (bkp[0] & S2_P1_MASK) >> S2_P1_SHIFT;
+                       p1[3] = (bkp[0] & S3_P1_MASK) >> S3_P1_SHIFT;
+                       p1[4] = (bkp[1] & S4_P1_MASK);
+                       p1[5] = (bkp[1] & S5_P1_MASK) >> S5_P1_SHIFT;
+                       p1[6] = (bkp[1] & S6_P1_MASK) >> S6_P1_SHIFT;
+                       p1[7] = (bkp[1] & S7_P1_MASK) >> S7_P1_SHIFT;
+                       p1[8] = (bkp[2] & S8_P1_MASK_BKP) >> S8_P1_SHIFT;
+                       p1[9] = (bkp[2] & S9_P1_MASK_BKP) >> S9_P1_BKP_SHIFT;
+                       p1[10] = (bkp[2] & S10_P1_MASK_BKP) >> S10_P1_BKP_SHIFT;
+                       break;
+               }
+       } else {
+               mode = (calib[1] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
+               mode |= (calib[3] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
+
+               switch (mode) {
+               case TWO_PT_CALIB:
+                       base2 = (calib[2] & BASE2_MASK) >> BASE2_SHIFT;
+                       p2[0] = (calib[2] & S0_P2_MASK) >> S0_P2_SHIFT;
+                       p2[1] = (calib[2] & S1_P2_MASK) >> S1_P2_SHIFT;
+                       p2[2] = (calib[3] & S2_P2_MASK);
+                       p2[3] = (calib[3] & S3_P2_MASK) >> S3_P2_SHIFT;
+                       p2[4] = (calib[3] & S4_P2_MASK) >> S4_P2_SHIFT;
+                       p2[5] = (calib[3] & S5_P2_MASK) >> S5_P2_SHIFT;
+                       p2[6] = (calib[3] & S6_P2_MASK) >> S6_P2_SHIFT;
+                       p2[7] = (calib[4] & S7_P2_MASK);
+                       p2[8] = (calib[4] & S8_P2_MASK) >> S8_P2_SHIFT;
+                       p2[9] = (calib[4] & S9_P2_MASK) >> S9_P2_SHIFT;
+                       p2[10] = (calib[4] & S10_P2_MASK) >> S10_P2_SHIFT;
+                       /* Fall through */
+               case ONE_PT_CALIB:
+               case ONE_PT_CALIB2:
+                       base1 = calib[0] & BASE1_MASK;
+                       p1[0] = (calib[0] & S0_P1_MASK) >> S0_P1_SHIFT;
+                       p1[1] = (calib[0] & S1_P1_MASK) >> S1_P1_SHIFT;
+                       p1[2] = (calib[0] & S2_P1_MASK) >> S2_P1_SHIFT;
+                       p1[3] = (calib[0] & S3_P1_MASK) >> S3_P1_SHIFT;
+                       p1[4] = (calib[1] & S4_P1_MASK);
+                       p1[5] = (calib[1] & S5_P1_MASK) >> S5_P1_SHIFT;
+                       p1[6] = (calib[1] & S6_P1_MASK) >> S6_P1_SHIFT;
+                       p1[7] = (calib[1] & S7_P1_MASK) >> S7_P1_SHIFT;
+                       p1[8] = (calib[1] & S8_P1_MASK) >> S8_P1_SHIFT;
+                       p1[9] = (calib[2] & S9_P1_MASK);
+                       p1[10] = (calib[2] & S10_P1_MASK) >> S10_P1_SHIFT;
+                       break;
+               }
+       }
+
+       switch (mode) {
+       case ONE_PT_CALIB:
+               for (i = 0; i < tmdev->num_sensors; i++)
+                       p1[i] += (base1 << 2) | BIT_APPEND;
+               break;
+       case TWO_PT_CALIB:
+               for (i = 0; i < tmdev->num_sensors; i++) {
+                       p2[i] += base2;
+                       p2[i] <<= 2;
+                       p2[i] |= BIT_APPEND;
+               }
+               /* Fall through */
+       case ONE_PT_CALIB2:
+               for (i = 0; i < tmdev->num_sensors; i++) {
+                       p1[i] += base1;
+                       p1[i] <<= 2;
+                       p1[i] |= BIT_APPEND;
+               }
+               break;
+       default:
+               for (i = 0; i < tmdev->num_sensors; i++)
+                       p2[i] = 780;
+               p1[0] = 502;
+               p1[1] = 509;
+               p1[2] = 503;
+               p1[3] = 509;
+               p1[4] = 505;
+               p1[5] = 509;
+               p1[6] = 507;
+               p1[7] = 510;
+               p1[8] = 508;
+               p1[9] = 509;
+               p1[10] = 508;
+               break;
+       }
+
+       compute_intercept_slope(tmdev, p1, p2, mode);
+
+       return 0;
+}
+
+static const struct tsens_ops ops_8974 = {
+       .init           = init_common,
+       .calibrate      = calibrate_8974,
+       .get_temp       = get_temp_common,
+};
+
+const struct tsens_data data_8974 = {
+       .num_sensors    = 11,
+       .ops            = &ops_8974,
+};
diff --git a/drivers/thermal/qcom/tsens-8996.c b/drivers/thermal/qcom/tsens-8996.c
new file mode 100644 (file)
index 0000000..e1f7781
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include "tsens.h"
+
+#define STATUS_OFFSET  0x10a0
+#define LAST_TEMP_MASK 0xfff
+#define STATUS_VALID_BIT       BIT(21)
+#define CODE_SIGN_BIT          BIT(11)
+
+static int get_temp_8996(struct tsens_device *tmdev, int id, int *temp)
+{
+       struct tsens_sensor *s = &tmdev->sensor[id];
+       u32 code;
+       unsigned int sensor_addr;
+       int last_temp = 0, last_temp2 = 0, last_temp3 = 0, ret;
+
+       sensor_addr = STATUS_OFFSET + s->hw_id * 4;
+       ret = regmap_read(tmdev->map, sensor_addr, &code);
+       if (ret)
+               return ret;
+       last_temp = code & LAST_TEMP_MASK;
+       if (code & STATUS_VALID_BIT)
+               goto done;
+
+       /* Try a second time */
+       ret = regmap_read(tmdev->map, sensor_addr, &code);
+       if (ret)
+               return ret;
+       if (code & STATUS_VALID_BIT) {
+               last_temp = code & LAST_TEMP_MASK;
+               goto done;
+       } else {
+               last_temp2 = code & LAST_TEMP_MASK;
+       }
+
+       /* Try a third/last time */
+       ret = regmap_read(tmdev->map, sensor_addr, &code);
+       if (ret)
+               return ret;
+       if (code & STATUS_VALID_BIT) {
+               last_temp = code & LAST_TEMP_MASK;
+               goto done;
+       } else {
+               last_temp3 = code & LAST_TEMP_MASK;
+       }
+
+       if (last_temp == last_temp2)
+               last_temp = last_temp2;
+       else if (last_temp2 == last_temp3)
+               last_temp = last_temp3;
+done:
+       /* Code sign bit is the sign extension for a negative value */
+       if (last_temp & CODE_SIGN_BIT)
+               last_temp |= ~CODE_SIGN_BIT;
+
+       /* Temperatures are in deciCelicius */
+       *temp = last_temp * 100;
+
+       return 0;
+}
+
+static const struct tsens_ops ops_8996 = {
+       .init           = init_common,
+       .get_temp       = get_temp_8996,
+};
+
+const struct tsens_data data_8996 = {
+       .num_sensors    = 13,
+       .ops            = &ops_8996,
+};
diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c
new file mode 100644 (file)
index 0000000..b1449ad
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include "tsens.h"
+
+#define S0_ST_ADDR             0x1030
+#define SN_ADDR_OFFSET         0x4
+#define SN_ST_TEMP_MASK                0x3ff
+#define CAL_DEGC_PT1           30
+#define CAL_DEGC_PT2           120
+#define SLOPE_FACTOR           1000
+#define SLOPE_DEFAULT          3200
+
+char *qfprom_read(struct device *dev, const char *cname)
+{
+       struct nvmem_cell *cell;
+       ssize_t data;
+       char *ret;
+
+       cell = nvmem_cell_get(dev, cname);
+       if (IS_ERR(cell))
+               return ERR_CAST(cell);
+
+       ret = nvmem_cell_read(cell, &data);
+       nvmem_cell_put(cell);
+
+       return ret;
+}
+
+/*
+ * Use this function on devices where slope and offset calculations
+ * depend on calibration data read from qfprom. On others the slope
+ * and offset values are derived from tz->tzp->slope and tz->tzp->offset
+ * resp.
+ */
+void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
+                            u32 *p2, u32 mode)
+{
+       int i;
+       int num, den;
+
+       for (i = 0; i < tmdev->num_sensors; i++) {
+               dev_dbg(tmdev->dev,
+                       "sensor%d - data_point1:%#x data_point2:%#x\n",
+                       i, p1[i], p2[i]);
+
+               tmdev->sensor[i].slope = SLOPE_DEFAULT;
+               if (mode == TWO_PT_CALIB) {
+                       /*
+                        * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
+                        *      temp_120_degc - temp_30_degc (x2 - x1)
+                        */
+                       num = p2[i] - p1[i];
+                       num *= SLOPE_FACTOR;
+                       den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
+                       tmdev->sensor[i].slope = num / den;
+               }
+
+               tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
+                               (CAL_DEGC_PT1 *
+                               tmdev->sensor[i].slope);
+               dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset);
+       }
+}
+
+static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
+{
+       int degc, num, den;
+
+       num = (adc_code * SLOPE_FACTOR) - s->offset;
+       den = s->slope;
+
+       if (num > 0)
+               degc = num + (den / 2);
+       else if (num < 0)
+               degc = num - (den / 2);
+       else
+               degc = num;
+
+       degc /= den;
+
+       return degc;
+}
+
+int get_temp_common(struct tsens_device *tmdev, int id, int *temp)
+{
+       struct tsens_sensor *s = &tmdev->sensor[id];
+       u32 code;
+       unsigned int sensor_addr;
+       int last_temp = 0, ret;
+
+       sensor_addr = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET;
+       ret = regmap_read(tmdev->map, sensor_addr, &code);
+       if (ret)
+               return ret;
+       last_temp = code & SN_ST_TEMP_MASK;
+
+       *temp = code_to_degc(last_temp, s) * 1000;
+
+       return 0;
+}
+
+static const struct regmap_config tsens_config = {
+       .reg_bits       = 32,
+       .val_bits       = 32,
+       .reg_stride     = 4,
+};
+
+int __init init_common(struct tsens_device *tmdev)
+{
+       void __iomem *base;
+
+       base = of_iomap(tmdev->dev->of_node, 0);
+       if (!base)
+               return -EINVAL;
+
+       tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config);
+       if (IS_ERR(tmdev->map)) {
+               iounmap(base);
+               return PTR_ERR(tmdev->map);
+       }
+
+       return 0;
+}
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
new file mode 100644 (file)
index 0000000..3f9fe6a
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include "tsens.h"
+
+static int tsens_get_temp(void *data, int *temp)
+{
+       const struct tsens_sensor *s = data;
+       struct tsens_device *tmdev = s->tmdev;
+
+       return tmdev->ops->get_temp(tmdev, s->id, temp);
+}
+
+static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend)
+{
+       const struct tsens_sensor *s = p;
+       struct tsens_device *tmdev = s->tmdev;
+
+       if (tmdev->ops->get_trend)
+               return  tmdev->ops->get_trend(tmdev, s->id, trend);
+
+       return -ENOTSUPP;
+}
+
+static int  __maybe_unused tsens_suspend(struct device *dev)
+{
+       struct tsens_device *tmdev = dev_get_drvdata(dev);
+
+       if (tmdev->ops && tmdev->ops->suspend)
+               return tmdev->ops->suspend(tmdev);
+
+       return 0;
+}
+
+static int __maybe_unused tsens_resume(struct device *dev)
+{
+       struct tsens_device *tmdev = dev_get_drvdata(dev);
+
+       if (tmdev->ops && tmdev->ops->resume)
+               return tmdev->ops->resume(tmdev);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
+
+static const struct of_device_id tsens_table[] = {
+       {
+               .compatible = "qcom,msm8916-tsens",
+               .data = &data_8916,
+       }, {
+               .compatible = "qcom,msm8974-tsens",
+               .data = &data_8974,
+       }, {
+               .compatible = "qcom,msm8996-tsens",
+               .data = &data_8996,
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, tsens_table);
+
+static const struct thermal_zone_of_device_ops tsens_of_ops = {
+       .get_temp = tsens_get_temp,
+       .get_trend = tsens_get_trend,
+};
+
+static int tsens_register(struct tsens_device *tmdev)
+{
+       int i;
+       struct thermal_zone_device *tzd;
+       u32 *hw_id, n = tmdev->num_sensors;
+
+       hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL);
+       if (!hw_id)
+               return -ENOMEM;
+
+       for (i = 0;  i < tmdev->num_sensors; i++) {
+               tmdev->sensor[i].tmdev = tmdev;
+               tmdev->sensor[i].id = i;
+               tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i,
+                                                          &tmdev->sensor[i],
+                                                          &tsens_of_ops);
+               if (IS_ERR(tzd))
+                       continue;
+               tmdev->sensor[i].tzd = tzd;
+               if (tmdev->ops->enable)
+                       tmdev->ops->enable(tmdev, i);
+       }
+       return 0;
+}
+
+static int tsens_probe(struct platform_device *pdev)
+{
+       int ret, i;
+       struct device *dev;
+       struct device_node *np;
+       struct tsens_sensor *s;
+       struct tsens_device *tmdev;
+       const struct tsens_data *data;
+       const struct of_device_id *id;
+
+       if (pdev->dev.of_node)
+               dev = &pdev->dev;
+       else
+               dev = pdev->dev.parent;
+
+       np = dev->of_node;
+
+       id = of_match_node(tsens_table, np);
+       if (id)
+               data = id->data;
+       else
+               data = &data_8960;
+
+       if (data->num_sensors <= 0) {
+               dev_err(dev, "invalid number of sensors\n");
+               return -EINVAL;
+       }
+
+       tmdev = devm_kzalloc(dev, sizeof(*tmdev) +
+                            data->num_sensors * sizeof(*s), GFP_KERNEL);
+       if (!tmdev)
+               return -ENOMEM;
+
+       tmdev->dev = dev;
+       tmdev->num_sensors = data->num_sensors;
+       tmdev->ops = data->ops;
+       for (i = 0;  i < tmdev->num_sensors; i++) {
+               if (data->hw_ids)
+                       tmdev->sensor[i].hw_id = data->hw_ids[i];
+               else
+                       tmdev->sensor[i].hw_id = i;
+       }
+
+       if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp)
+               return -EINVAL;
+
+       ret = tmdev->ops->init(tmdev);
+       if (ret < 0) {
+               dev_err(dev, "tsens init failed\n");
+               return ret;
+       }
+
+       if (tmdev->ops->calibrate) {
+               ret = tmdev->ops->calibrate(tmdev);
+               if (ret < 0) {
+                       dev_err(dev, "tsens calibration failed\n");
+                       return ret;
+               }
+       }
+
+       ret = tsens_register(tmdev);
+
+       platform_set_drvdata(pdev, tmdev);
+
+       return ret;
+}
+
+static int tsens_remove(struct platform_device *pdev)
+{
+       struct tsens_device *tmdev = platform_get_drvdata(pdev);
+
+       if (tmdev->ops->disable)
+               tmdev->ops->disable(tmdev);
+
+       return 0;
+}
+
+static struct platform_driver tsens_driver = {
+       .probe = tsens_probe,
+       .remove = tsens_remove,
+       .driver = {
+               .name = "qcom-tsens",
+               .pm     = &tsens_pm_ops,
+               .of_match_table = tsens_table,
+       },
+};
+module_platform_driver(tsens_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
+MODULE_ALIAS("platform:qcom-tsens");
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
new file mode 100644 (file)
index 0000000..911c197
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. 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.
+ */
+#ifndef __QCOM_TSENS_H__
+#define __QCOM_TSENS_H__
+
+#define ONE_PT_CALIB           0x1
+#define ONE_PT_CALIB2          0x2
+#define TWO_PT_CALIB           0x3
+
+#include <linux/thermal.h>
+
+struct tsens_device;
+
+struct tsens_sensor {
+       struct tsens_device             *tmdev;
+       struct thermal_zone_device      *tzd;
+       int                             offset;
+       int                             id;
+       int                             hw_id;
+       int                             slope;
+       u32                             status;
+};
+
+/**
+ * struct tsens_ops - operations as supported by the tsens device
+ * @init: Function to initialize the tsens device
+ * @calibrate: Function to calibrate the tsens device
+ * @get_temp: Function which returns the temp in millidegC
+ * @enable: Function to enable (clocks/power) tsens device
+ * @disable: Function to disable the tsens device
+ * @suspend: Function to suspend the tsens device
+ * @resume: Function to resume the tsens device
+ * @get_trend: Function to get the thermal/temp trend
+ */
+struct tsens_ops {
+       /* mandatory callbacks */
+       int (*init)(struct tsens_device *);
+       int (*calibrate)(struct tsens_device *);
+       int (*get_temp)(struct tsens_device *, int, int *);
+       /* optional callbacks */
+       int (*enable)(struct tsens_device *, int);
+       void (*disable)(struct tsens_device *);
+       int (*suspend)(struct tsens_device *);
+       int (*resume)(struct tsens_device *);
+       int (*get_trend)(struct tsens_device *, int, enum thermal_trend *);
+};
+
+/**
+ * struct tsens_data - tsens instance specific data
+ * @num_sensors: Max number of sensors supported by platform
+ * @ops: operations the tsens instance supports
+ * @hw_ids: Subset of sensors ids supported by platform, if not the first n
+ */
+struct tsens_data {
+       const u32               num_sensors;
+       const struct tsens_ops  *ops;
+       unsigned int            *hw_ids;
+};
+
+/* Registers to be saved/restored across a context loss */
+struct tsens_context {
+       int     threshold;
+       int     control;
+};
+
+struct tsens_device {
+       struct device                   *dev;
+       u32                             num_sensors;
+       struct regmap                   *map;
+       struct regmap_field             *status_field;
+       struct tsens_context            ctx;
+       bool                            trdy;
+       const struct tsens_ops          *ops;
+       struct tsens_sensor             sensor[0];
+};
+
+char *qfprom_read(struct device *, const char *);
+void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32);
+int init_common(struct tsens_device *);
+int get_temp_common(struct tsens_device *, int, int *);
+
+extern const struct tsens_data data_8916, data_8974, data_8960, data_8996;
+
+#endif /* __QCOM_TSENS_H__ */
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
new file mode 100644 (file)
index 0000000..644ba52
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2016 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/thermal.h>
+
+#include "thermal_core.h"
+
+#define SITES_MAX      16
+
+/*
+ * QorIQ TMU Registers
+ */
+struct qoriq_tmu_site_regs {
+       u32 tritsr;             /* Immediate Temperature Site Register */
+       u32 tratsr;             /* Average Temperature Site Register */
+       u8 res0[0x8];
+};
+
+struct qoriq_tmu_regs {
+       u32 tmr;                /* Mode Register */
+#define TMR_DISABLE    0x0
+#define TMR_ME         0x80000000
+#define TMR_ALPF       0x0c000000
+       u32 tsr;                /* Status Register */
+       u32 tmtmir;             /* Temperature measurement interval Register */
+#define TMTMIR_DEFAULT 0x0000000f
+       u8 res0[0x14];
+       u32 tier;               /* Interrupt Enable Register */
+#define TIER_DISABLE   0x0
+       u32 tidr;               /* Interrupt Detect Register */
+       u32 tiscr;              /* Interrupt Site Capture Register */
+       u32 ticscr;             /* Interrupt Critical Site Capture Register */
+       u8 res1[0x10];
+       u32 tmhtcrh;            /* High Temperature Capture Register */
+       u32 tmhtcrl;            /* Low Temperature Capture Register */
+       u8 res2[0x8];
+       u32 tmhtitr;            /* High Temperature Immediate Threshold */
+       u32 tmhtatr;            /* High Temperature Average Threshold */
+       u32 tmhtactr;   /* High Temperature Average Crit Threshold */
+       u8 res3[0x24];
+       u32 ttcfgr;             /* Temperature Configuration Register */
+       u32 tscfgr;             /* Sensor Configuration Register */
+       u8 res4[0x78];
+       struct qoriq_tmu_site_regs site[SITES_MAX];
+       u8 res5[0x9f8];
+       u32 ipbrr0;             /* IP Block Revision Register 0 */
+       u32 ipbrr1;             /* IP Block Revision Register 1 */
+       u8 res6[0x310];
+       u32 ttr0cr;             /* Temperature Range 0 Control Register */
+       u32 ttr1cr;             /* Temperature Range 1 Control Register */
+       u32 ttr2cr;             /* Temperature Range 2 Control Register */
+       u32 ttr3cr;             /* Temperature Range 3 Control Register */
+};
+
+/*
+ * Thermal zone data
+ */
+struct qoriq_tmu_data {
+       struct thermal_zone_device *tz;
+       struct qoriq_tmu_regs __iomem *regs;
+       int sensor_id;
+       bool little_endian;
+};
+
+static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr)
+{
+       if (p->little_endian)
+               iowrite32(val, addr);
+       else
+               iowrite32be(val, addr);
+}
+
+static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
+{
+       if (p->little_endian)
+               return ioread32(addr);
+       else
+               return ioread32be(addr);
+}
+
+static int tmu_get_temp(void *p, int *temp)
+{
+       u32 val;
+       struct qoriq_tmu_data *data = p;
+
+       val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
+       *temp = (val & 0xff) * 1000;
+
+       return 0;
+}
+
+static int qoriq_tmu_get_sensor_id(void)
+{
+       int ret, id;
+       struct of_phandle_args sensor_specs;
+       struct device_node *np, *sensor_np;
+
+       np = of_find_node_by_name(NULL, "thermal-zones");
+       if (!np)
+               return -ENODEV;
+
+       sensor_np = of_get_next_child(np, NULL);
+       ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
+                       "#thermal-sensor-cells",
+                       0, &sensor_specs);
+       if (ret) {
+               of_node_put(np);
+               of_node_put(sensor_np);
+               return ret;
+       }
+
+       if (sensor_specs.args_count >= 1) {
+               id = sensor_specs.args[0];
+               WARN(sensor_specs.args_count > 1,
+                               "%s: too many cells in sensor specifier %d\n",
+                               sensor_specs.np->name, sensor_specs.args_count);
+       } else {
+               id = 0;
+       }
+
+       of_node_put(np);
+       of_node_put(sensor_np);
+
+       return id;
+}
+
+static int qoriq_tmu_calibration(struct platform_device *pdev)
+{
+       int i, val, len;
+       u32 range[4];
+       const u32 *calibration;
+       struct device_node *np = pdev->dev.of_node;
+       struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
+
+       if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
+               dev_err(&pdev->dev, "missing calibration range.\n");
+               return -ENODEV;
+       }
+
+       /* Init temperature range registers */
+       tmu_write(data, range[0], &data->regs->ttr0cr);
+       tmu_write(data, range[1], &data->regs->ttr1cr);
+       tmu_write(data, range[2], &data->regs->ttr2cr);
+       tmu_write(data, range[3], &data->regs->ttr3cr);
+
+       calibration = of_get_property(np, "fsl,tmu-calibration", &len);
+       if (calibration == NULL || len % 8) {
+               dev_err(&pdev->dev, "invalid calibration data.\n");
+               return -ENODEV;
+       }
+
+       for (i = 0; i < len; i += 8, calibration += 2) {
+               val = of_read_number(calibration, 1);
+               tmu_write(data, val, &data->regs->ttcfgr);
+               val = of_read_number(calibration + 1, 1);
+               tmu_write(data, val, &data->regs->tscfgr);
+       }
+
+       return 0;
+}
+
+static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
+{
+       /* Disable interrupt, using polling instead */
+       tmu_write(data, TIER_DISABLE, &data->regs->tier);
+
+       /* Set update_interval */
+       tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
+
+       /* Disable monitoring */
+       tmu_write(data, TMR_DISABLE, &data->regs->tmr);
+}
+
+static struct thermal_zone_of_device_ops tmu_tz_ops = {
+       .get_temp = tmu_get_temp,
+};
+
+static int qoriq_tmu_probe(struct platform_device *pdev)
+{
+       int ret;
+       const struct thermal_trip *trip;
+       struct qoriq_tmu_data *data;
+       struct device_node *np = pdev->dev.of_node;
+       u32 site = 0;
+
+       if (!np) {
+               dev_err(&pdev->dev, "Device OF-Node is NULL");
+               return -ENODEV;
+       }
+
+       data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
+                           GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, data);
+
+       data->little_endian = of_property_read_bool(np, "little-endian");
+
+       data->sensor_id = qoriq_tmu_get_sensor_id();
+       if (data->sensor_id < 0) {
+               dev_err(&pdev->dev, "Failed to get sensor id\n");
+               ret = -ENODEV;
+               goto err_iomap;
+       }
+
+       data->regs = of_iomap(np, 0);
+       if (!data->regs) {
+               dev_err(&pdev->dev, "Failed to get memory region\n");
+               ret = -ENODEV;
+               goto err_iomap;
+       }
+
+       qoriq_tmu_init_device(data);    /* TMU initialization */
+
+       ret = qoriq_tmu_calibration(pdev);      /* TMU calibration */
+       if (ret < 0)
+               goto err_tmu;
+
+       data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
+                               data, &tmu_tz_ops);
+       if (IS_ERR(data->tz)) {
+               ret = PTR_ERR(data->tz);
+               dev_err(&pdev->dev,
+                       "Failed to register thermal zone device %d\n", ret);
+               goto err_tmu;
+       }
+
+       trip = of_thermal_get_trip_points(data->tz);
+
+       /* Enable monitoring */
+       site |= 0x1 << (15 - data->sensor_id);
+       tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
+
+       return 0;
+
+err_tmu:
+       iounmap(data->regs);
+
+err_iomap:
+       platform_set_drvdata(pdev, NULL);
+
+       return ret;
+}
+
+static int qoriq_tmu_remove(struct platform_device *pdev)
+{
+       struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
+
+       thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
+
+       /* Disable monitoring */
+       tmu_write(data, TMR_DISABLE, &data->regs->tmr);
+
+       iounmap(data->regs);
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qoriq_tmu_suspend(struct device *dev)
+{
+       u32 tmr;
+       struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+       /* Disable monitoring */
+       tmr = tmu_read(data, &data->regs->tmr);
+       tmr &= ~TMR_ME;
+       tmu_write(data, tmr, &data->regs->tmr);
+
+       return 0;
+}
+
+static int qoriq_tmu_resume(struct device *dev)
+{
+       u32 tmr;
+       struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+       /* Enable monitoring */
+       tmr = tmu_read(data, &data->regs->tmr);
+       tmr |= TMR_ME;
+       tmu_write(data, tmr, &data->regs->tmr);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
+                        qoriq_tmu_suspend, qoriq_tmu_resume);
+
+static const struct of_device_id qoriq_tmu_match[] = {
+       { .compatible = "fsl,qoriq-tmu", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
+
+static struct platform_driver qoriq_tmu = {
+       .driver = {
+               .name           = "qoriq_thermal",
+               .pm             = &qoriq_tmu_pm_ops,
+               .of_match_table = qoriq_tmu_match,
+       },
+       .probe  = qoriq_tmu_probe,
+       .remove = qoriq_tmu_remove,
+};
+module_platform_driver(qoriq_tmu);
+
+MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
+MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
+MODULE_LICENSE("GPL v2");
index 5f817923f374f5f08412bda03876c831da68b697..73e5fee6cf1d55ddd67729180564f06559da8b59 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/spinlock.h>
 #include <linux/thermal.h>
 
+#include "thermal_hwmon.h"
+
 #define IDLE_INTERVAL  5000
 
 #define COMMON_STR     0x00
@@ -75,6 +77,8 @@ struct rcar_thermal_priv {
 #define rcar_priv_to_dev(priv)         ((priv)->common->dev)
 #define rcar_has_irq_support(priv)     ((priv)->common->base)
 #define rcar_id_to_shift(priv)         ((priv)->id * 8)
+#define rcar_of_data(dev)              ((unsigned long)of_device_get_match_data(dev))
+#define rcar_use_of_thermal(dev)       (rcar_of_data(dev) == USE_OF_THERMAL)
 
 #define USE_OF_THERMAL 1
 static const struct of_device_id rcar_thermal_dt_ids[] = {
@@ -354,7 +358,8 @@ static void rcar_thermal_work(struct work_struct *work)
                return;
 
        if (nctemp != cctemp)
-               thermal_zone_device_update(priv->zone);
+               thermal_zone_device_update(priv->zone,
+                                          THERMAL_EVENT_UNSPECIFIED);
 }
 
 static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status)
@@ -415,7 +420,10 @@ static int rcar_thermal_remove(struct platform_device *pdev)
 
        rcar_thermal_for_each_priv(priv, common) {
                rcar_thermal_irq_disable(priv);
-               thermal_zone_device_unregister(priv->zone);
+               if (rcar_use_of_thermal(dev))
+                       thermal_remove_hwmon_sysfs(priv->zone);
+               else
+                       thermal_zone_device_unregister(priv->zone);
        }
 
        pm_runtime_put(dev);
@@ -430,7 +438,6 @@ static int rcar_thermal_probe(struct platform_device *pdev)
        struct rcar_thermal_priv *priv;
        struct device *dev = &pdev->dev;
        struct resource *res, *irq;
-       unsigned long of_data = (unsigned long)of_device_get_match_data(dev);
        int mres = 0;
        int i;
        int ret = -ENODEV;
@@ -491,7 +498,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
                if (ret < 0)
                        goto error_unregister;
 
-               if (of_data == USE_OF_THERMAL)
+               if (rcar_use_of_thermal(dev))
                        priv->zone = devm_thermal_zone_of_sensor_register(
                                                dev, i, priv,
                                                &rcar_thermal_zone_of_ops);
@@ -508,6 +515,17 @@ static int rcar_thermal_probe(struct platform_device *pdev)
                        goto error_unregister;
                }
 
+               if (rcar_use_of_thermal(dev)) {
+                       /*
+                        * thermal_zone doesn't enable hwmon as default,
+                        * but, enable it here to keep compatible
+                        */
+                       priv->zone->tzp->no_hwmon = false;
+                       ret = thermal_add_hwmon_sysfs(priv->zone);
+                       if (ret)
+                               goto error_unregister;
+               }
+
                rcar_thermal_irq_enable(priv);
 
                list_move_tail(&priv->list, &common->head);
index 5d491f16a866c24607bf87a0eaf12a7f399ac72f..e227a9f0acf71c52917a91b8986c9fc4c4d17e31 100644 (file)
@@ -96,6 +96,7 @@ struct chip_tsadc_table {
  * @initialize: SoC special initialize tsadc controller method
  * @irq_ack: clear the interrupt
  * @get_temp: get the temperature
+ * @set_alarm_temp: set the high temperature interrupt
  * @set_tshut_temp: set the hardware-controlled shutdown temperature
  * @set_tshut_mode: set the hardware-controlled shutdown mode
  * @table: the chip-specific conversion table
@@ -119,6 +120,8 @@ struct rockchip_tsadc_chip {
        /* Per-sensor methods */
        int (*get_temp)(struct chip_tsadc_table table,
                        int chn, void __iomem *reg, int *temp);
+       void (*set_alarm_temp)(struct chip_tsadc_table table,
+                              int chn, void __iomem *reg, int temp);
        void (*set_tshut_temp)(struct chip_tsadc_table table,
                               int chn, void __iomem *reg, int temp);
        void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);
@@ -183,6 +186,7 @@ struct rockchip_thermal_data {
 #define TSADCV2_INT_EN                         0x08
 #define TSADCV2_INT_PD                         0x0c
 #define TSADCV2_DATA(chn)                      (0x20 + (chn) * 0x04)
+#define TSADCV2_COMP_INT(chn)                  (0x30 + (chn) * 0x04)
 #define TSADCV2_COMP_SHUT(chn)                 (0x40 + (chn) * 0x04)
 #define TSADCV2_HIGHT_INT_DEBOUNCE             0x60
 #define TSADCV2_HIGHT_TSHUT_DEBOUNCE           0x64
@@ -207,18 +211,21 @@ struct rockchip_thermal_data {
 
 #define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT       4
 #define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT     4
-#define TSADCV2_AUTO_PERIOD_TIME               250 /* msec */
-#define TSADCV2_AUTO_PERIOD_HT_TIME            50  /* msec */
+#define TSADCV2_AUTO_PERIOD_TIME               250 /* 250ms */
+#define TSADCV2_AUTO_PERIOD_HT_TIME            50  /* 50ms */
+#define TSADCV3_AUTO_PERIOD_TIME               1875 /* 2.5ms */
+#define TSADCV3_AUTO_PERIOD_HT_TIME            1875 /* 2.5ms */
+
 #define TSADCV2_USER_INTER_PD_SOC              0x340 /* 13 clocks */
 
 #define GRF_SARADC_TESTBIT                     0x0e644
 #define GRF_TSADC_TESTBIT_L                    0x0e648
 #define GRF_TSADC_TESTBIT_H                    0x0e64c
 
-#define GRF_TSADC_TSEN_PD_ON                   (0x30003 << 0)
-#define GRF_TSADC_TSEN_PD_OFF                  (0x30000 << 0)
 #define GRF_SARADC_TESTBIT_ON                  (0x10001 << 2)
 #define GRF_TSADC_TESTBIT_H_ON                 (0x10001 << 2)
+#define GRF_TSADC_VCM_EN_L                     (0x10001 << 7)
+#define GRF_TSADC_VCM_EN_H                     (0x10001 << 7)
 
 /**
  * struct tsadc_table - code to temperature conversion table
@@ -394,13 +401,17 @@ static u32 rk_tsadcv2_temp_to_code(struct chip_tsadc_table table,
                                   int temp)
 {
        int high, low, mid;
+       u32 error = 0;
 
        low = 0;
        high = table.length - 1;
        mid = (high + low) / 2;
 
-       if (temp < table.id[low].temp || temp > table.id[high].temp)
-               return 0;
+       /* Return mask code data when the temp is over table range */
+       if (temp < table.id[low].temp || temp > table.id[high].temp) {
+               error = table.data_mask;
+               goto exit;
+       }
 
        while (low <= high) {
                if (temp == table.id[mid].temp)
@@ -412,7 +423,9 @@ static u32 rk_tsadcv2_temp_to_code(struct chip_tsadc_table table,
                mid = (low + high) / 2;
        }
 
-       return 0;
+exit:
+       pr_err("Invalid the conversion, error=%d\n", error);
+       return error;
 }
 
 static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code,
@@ -543,14 +556,34 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs,
                /* Set interleave value to workround ic time sync issue */
                writel_relaxed(TSADCV2_USER_INTER_PD_SOC, regs +
                               TSADCV2_USER_CON);
+
+               writel_relaxed(TSADCV2_AUTO_PERIOD_TIME,
+                              regs + TSADCV2_AUTO_PERIOD);
+               writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
+                              regs + TSADCV2_HIGHT_INT_DEBOUNCE);
+               writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME,
+                              regs + TSADCV2_AUTO_PERIOD_HT);
+               writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
+                              regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
+
        } else {
-               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_ON);
-               mdelay(10);
-               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_OFF);
+               /* Enable the voltage common mode feature */
+               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_VCM_EN_L);
+               regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_VCM_EN_H);
+
                usleep_range(15, 100); /* The spec note says at least 15 us */
                regmap_write(grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON);
                regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON);
                usleep_range(90, 200); /* The spec note says at least 90 us */
+
+               writel_relaxed(TSADCV3_AUTO_PERIOD_TIME,
+                              regs + TSADCV2_AUTO_PERIOD);
+               writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
+                              regs + TSADCV2_HIGHT_INT_DEBOUNCE);
+               writel_relaxed(TSADCV3_AUTO_PERIOD_HT_TIME,
+                              regs + TSADCV2_AUTO_PERIOD_HT);
+               writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
+                              regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
        }
 
        if (tshut_polarity == TSHUT_HIGH_ACTIVE)
@@ -559,14 +592,6 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs,
        else
                writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
                               regs + TSADCV2_AUTO_CON);
-
-       writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD);
-       writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
-                      regs + TSADCV2_HIGHT_INT_DEBOUNCE);
-       writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME,
-                      regs + TSADCV2_AUTO_PERIOD_HT);
-       writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
-                      regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
 }
 
 static void rk_tsadcv2_irq_ack(void __iomem *regs)
@@ -628,12 +653,34 @@ static int rk_tsadcv2_get_temp(struct chip_tsadc_table table,
        return rk_tsadcv2_code_to_temp(table, val, temp);
 }
 
+static void rk_tsadcv2_alarm_temp(struct chip_tsadc_table table,
+                                 int chn, void __iomem *regs, int temp)
+{
+       u32 alarm_value, int_en;
+
+       /* Make sure the value is valid */
+       alarm_value = rk_tsadcv2_temp_to_code(table, temp);
+       if (alarm_value == table.data_mask)
+               return;
+
+       writel_relaxed(alarm_value & table.data_mask,
+                      regs + TSADCV2_COMP_INT(chn));
+
+       int_en = readl_relaxed(regs + TSADCV2_INT_EN);
+       int_en |= TSADCV2_INT_SRC_EN(chn);
+       writel_relaxed(int_en, regs + TSADCV2_INT_EN);
+}
+
 static void rk_tsadcv2_tshut_temp(struct chip_tsadc_table table,
                                  int chn, void __iomem *regs, int temp)
 {
        u32 tshut_value, val;
 
+       /* Make sure the value is valid */
        tshut_value = rk_tsadcv2_temp_to_code(table, temp);
+       if (tshut_value == table.data_mask)
+               return;
+
        writel_relaxed(tshut_value, regs + TSADCV2_COMP_SHUT(chn));
 
        /* TSHUT will be valid */
@@ -670,6 +717,7 @@ static const struct rockchip_tsadc_chip rk3228_tsadc_data = {
        .irq_ack = rk_tsadcv3_irq_ack,
        .control = rk_tsadcv3_control,
        .get_temp = rk_tsadcv2_get_temp,
+       .set_alarm_temp = rk_tsadcv2_alarm_temp,
        .set_tshut_temp = rk_tsadcv2_tshut_temp,
        .set_tshut_mode = rk_tsadcv2_tshut_mode,
 
@@ -694,6 +742,7 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
        .irq_ack = rk_tsadcv2_irq_ack,
        .control = rk_tsadcv2_control,
        .get_temp = rk_tsadcv2_get_temp,
+       .set_alarm_temp = rk_tsadcv2_alarm_temp,
        .set_tshut_temp = rk_tsadcv2_tshut_temp,
        .set_tshut_mode = rk_tsadcv2_tshut_mode,
 
@@ -718,6 +767,7 @@ static const struct rockchip_tsadc_chip rk3366_tsadc_data = {
        .irq_ack = rk_tsadcv3_irq_ack,
        .control = rk_tsadcv3_control,
        .get_temp = rk_tsadcv2_get_temp,
+       .set_alarm_temp = rk_tsadcv2_alarm_temp,
        .set_tshut_temp = rk_tsadcv2_tshut_temp,
        .set_tshut_mode = rk_tsadcv2_tshut_mode,
 
@@ -742,6 +792,7 @@ static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
        .irq_ack = rk_tsadcv2_irq_ack,
        .control = rk_tsadcv2_control,
        .get_temp = rk_tsadcv2_get_temp,
+       .set_alarm_temp = rk_tsadcv2_alarm_temp,
        .set_tshut_temp = rk_tsadcv2_tshut_temp,
        .set_tshut_mode = rk_tsadcv2_tshut_mode,
 
@@ -766,6 +817,7 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
        .irq_ack = rk_tsadcv3_irq_ack,
        .control = rk_tsadcv3_control,
        .get_temp = rk_tsadcv2_get_temp,
+       .set_alarm_temp = rk_tsadcv2_alarm_temp,
        .set_tshut_temp = rk_tsadcv2_tshut_temp,
        .set_tshut_mode = rk_tsadcv2_tshut_mode,
 
@@ -821,11 +873,27 @@ static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev)
        thermal->chip->irq_ack(thermal->regs);
 
        for (i = 0; i < thermal->chip->chn_num; i++)
-               thermal_zone_device_update(thermal->sensors[i].tzd);
+               thermal_zone_device_update(thermal->sensors[i].tzd,
+                                          THERMAL_EVENT_UNSPECIFIED);
 
        return IRQ_HANDLED;
 }
 
+static int rockchip_thermal_set_trips(void *_sensor, int low, int high)
+{
+       struct rockchip_thermal_sensor *sensor = _sensor;
+       struct rockchip_thermal_data *thermal = sensor->thermal;
+       const struct rockchip_tsadc_chip *tsadc = thermal->chip;
+
+       dev_dbg(&thermal->pdev->dev, "%s: sensor %d: low: %d, high %d\n",
+               __func__, sensor->id, low, high);
+
+       tsadc->set_alarm_temp(tsadc->table,
+                             sensor->id, thermal->regs, high);
+
+       return 0;
+}
+
 static int rockchip_thermal_get_temp(void *_sensor, int *out_temp)
 {
        struct rockchip_thermal_sensor *sensor = _sensor;
@@ -843,6 +911,7 @@ static int rockchip_thermal_get_temp(void *_sensor, int *out_temp)
 
 static const struct thermal_zone_of_device_ops rockchip_of_thermal_ops = {
        .get_temp = rockchip_thermal_get_temp,
+       .set_trips = rockchip_thermal_set_trips,
 };
 
 static int rockchip_configure_from_dt(struct device *dev,
index f3ce94ec73b513595e1297a0a222cd8cb3efa5c3..ad1186dd6132c3ca74f7df209605a25745b16cac 100644 (file)
@@ -225,7 +225,7 @@ static void exynos_report_trigger(struct exynos_tmu_data *p)
                return;
        }
 
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        mutex_lock(&tz->lock);
        /* Find the level for which trip happened */
index fc0c9e198710327bbd10983b895eac4d740bb258..91d42319de27279c8e8367e09dac108d6cabc561 100644 (file)
@@ -42,7 +42,8 @@ static irqreturn_t st_mmap_thermal_trip_handler(int irq, void *sdata)
 {
        struct st_thermal_sensor *sensor = sdata;
 
-       thermal_zone_device_update(sensor->thermal_dev);
+       thermal_zone_device_update(sensor->thermal_dev,
+                                  THERMAL_EVENT_UNSPECIFIED);
 
        return IRQ_HANDLED;
 }
index 70e0d9f406e902d4f9dc786e6adb5fc8dd271573..201304aeafebcdde57b6c194b5670726dae109e8 100644 (file)
@@ -64,6 +64,12 @@ static const struct thermal_zone_of_device_ops ops = {
        .get_temp       = tango_get_temp,
 };
 
+static void tango_thermal_init(struct tango_thermal_priv *priv)
+{
+       writel(0, priv->base + TEMPSI_CFG);
+       writel(CMD_ON, priv->base + TEMPSI_CMD);
+}
+
 static int tango_thermal_probe(struct platform_device *pdev)
 {
        struct resource *res;
@@ -79,14 +85,22 @@ static int tango_thermal_probe(struct platform_device *pdev)
        if (IS_ERR(priv->base))
                return PTR_ERR(priv->base);
 
+       platform_set_drvdata(pdev, priv);
        priv->thresh_idx = IDX_MIN;
-       writel(0, priv->base + TEMPSI_CFG);
-       writel(CMD_ON, priv->base + TEMPSI_CMD);
+       tango_thermal_init(priv);
 
        tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &ops);
        return PTR_ERR_OR_ZERO(tzdev);
 }
 
+static int __maybe_unused tango_thermal_resume(struct device *dev)
+{
+       tango_thermal_init(dev_get_drvdata(dev));
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(tango_thermal_pm, NULL, tango_thermal_resume);
+
 static const struct of_device_id tango_sensor_ids[] = {
        {
                .compatible = "sigma,smp8758-thermal",
@@ -99,6 +113,7 @@ static struct platform_driver tango_thermal_driver = {
        .driver = {
                .name           = "tango-thermal",
                .of_match_table = tango_sensor_ids,
+               .pm             = &tango_thermal_pm,
        },
 };
 
index b8651726201eb8c26c9766e2628da44cafc65b30..7d2db23d71a32deab7c524cb0c9ad8466886018a 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <dt-bindings/thermal/tegra124-soctherm.h>
 
+#include "../thermal_core.h"
 #include "soctherm.h"
 
 #define SENSOR_CONFIG0                         0
 #define READBACK_ADD_HALF                      BIT(7)
 #define READBACK_NEGATE                                BIT(0)
 
+/*
+ * THERMCTL_LEVEL0_GROUP_CPU is defined in soctherm.h
+ * because it will be used by tegraxxx_soctherm.c
+ */
+#define THERMCTL_LVL0_CPU0_EN_MASK             BIT(8)
+#define THERMCTL_LVL0_CPU0_CPU_THROT_MASK      (0x3 << 5)
+#define THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT     0x1
+#define THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY     0x2
+#define THERMCTL_LVL0_CPU0_GPU_THROT_MASK      (0x3 << 3)
+#define THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT     0x1
+#define THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY     0x2
+#define THERMCTL_LVL0_CPU0_MEM_THROT_MASK      BIT(2)
+#define THERMCTL_LVL0_CPU0_STATUS_MASK         0x3
+
+#define THERMCTL_LVL0_UP_STATS                 0x10
+#define THERMCTL_LVL0_DN_STATS                 0x14
+
+#define THERMCTL_STATS_CTL                     0x94
+#define STATS_CTL_CLR_DN                       0x8
+#define STATS_CTL_EN_DN                                0x4
+#define STATS_CTL_CLR_UP                       0x2
+#define STATS_CTL_EN_UP                                0x1
+
+#define THROT_GLOBAL_CFG                       0x400
+#define THROT_GLOBAL_ENB_MASK                  BIT(0)
+
+#define CPU_PSKIP_STATUS                       0x418
+#define XPU_PSKIP_STATUS_M_MASK                        (0xff << 12)
+#define XPU_PSKIP_STATUS_N_MASK                        (0xff << 4)
+#define XPU_PSKIP_STATUS_SW_OVERRIDE_MASK      BIT(1)
+#define XPU_PSKIP_STATUS_ENABLED_MASK          BIT(0)
+
+#define THROT_PRIORITY_LOCK                    0x424
+#define THROT_PRIORITY_LOCK_PRIORITY_MASK      0xff
+
+#define THROT_STATUS                           0x428
+#define THROT_STATUS_BREACH_MASK               BIT(12)
+#define THROT_STATUS_STATE_MASK                        (0xff << 4)
+#define THROT_STATUS_ENABLED_MASK              BIT(0)
+
+#define THROT_PSKIP_CTRL_LITE_CPU              0x430
+#define THROT_PSKIP_CTRL_ENABLE_MASK            BIT(31)
+#define THROT_PSKIP_CTRL_DIVIDEND_MASK          (0xff << 8)
+#define THROT_PSKIP_CTRL_DIVISOR_MASK           0xff
+#define THROT_PSKIP_CTRL_VECT_GPU_MASK          (0x7 << 16)
+#define THROT_PSKIP_CTRL_VECT_CPU_MASK          (0x7 << 8)
+#define THROT_PSKIP_CTRL_VECT2_CPU_MASK         0x7
+
+#define THROT_VECT_NONE                                0x0 /* 3'b000 */
+#define THROT_VECT_LOW                         0x1 /* 3'b001 */
+#define THROT_VECT_MED                         0x3 /* 3'b011 */
+#define THROT_VECT_HIGH                                0x7 /* 3'b111 */
+
+#define THROT_PSKIP_RAMP_LITE_CPU              0x434
+#define THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK  BIT(31)
+#define THROT_PSKIP_RAMP_DURATION_MASK         (0xffff << 8)
+#define THROT_PSKIP_RAMP_STEP_MASK             0xff
+
+#define THROT_PRIORITY_LITE                    0x444
+#define THROT_PRIORITY_LITE_PRIO_MASK          0xff
+
+#define THROT_DELAY_LITE                       0x448
+#define THROT_DELAY_LITE_DELAY_MASK            0xff
+
+/* car register offsets needed for enabling HW throttling */
+#define CAR_SUPER_CCLKG_DIVIDER                        0x36c
+#define CDIVG_USE_THERM_CONTROLS_MASK          BIT(30)
+
+/* ccroc register offsets needed for enabling HW throttling for Tegra132 */
+#define CCROC_SUPER_CCLKG_DIVIDER              0x024
+
+#define CCROC_GLOBAL_CFG                       0x148
+
+#define CCROC_THROT_PSKIP_RAMP_CPU             0x150
+#define CCROC_THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK    BIT(31)
+#define CCROC_THROT_PSKIP_RAMP_DURATION_MASK   (0xffff << 8)
+#define CCROC_THROT_PSKIP_RAMP_STEP_MASK       0xff
+
+#define CCROC_THROT_PSKIP_CTRL_CPU             0x154
+#define CCROC_THROT_PSKIP_CTRL_ENB_MASK                BIT(31)
+#define CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK   (0xff << 8)
+#define CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK    0xff
+
 /* get val from register(r) mask bits(m) */
 #define REG_GET_MASK(r, m)     (((r) & (m)) >> (ffs(m) - 1))
 /* set val(v) to mask bits(m) of register(r) */
 #define REG_SET_MASK(r, m, v)  (((r) & ~(m)) | \
                                 (((v) & (m >> (ffs(m) - 1))) << (ffs(m) - 1)))
 
+/* get dividend from the depth */
+#define THROT_DEPTH_DIVIDEND(depth)    ((256 * (100 - (depth)) / 100) - 1)
+
+/* get THROT_PSKIP_xxx offset per LIGHT/HEAVY throt and CPU/GPU dev */
+#define THROT_OFFSET                   0x30
+#define THROT_PSKIP_CTRL(throt, dev)   (THROT_PSKIP_CTRL_LITE_CPU + \
+                                       (THROT_OFFSET * throt) + (8 * dev))
+#define THROT_PSKIP_RAMP(throt, dev)   (THROT_PSKIP_RAMP_LITE_CPU + \
+                                       (THROT_OFFSET * throt) + (8 * dev))
+
+/* get THROT_xxx_CTRL offset per LIGHT/HEAVY throt */
+#define THROT_PRIORITY_CTRL(throt)     (THROT_PRIORITY_LITE + \
+                                       (THROT_OFFSET * throt))
+#define THROT_DELAY_CTRL(throt)                (THROT_DELAY_LITE + \
+                                       (THROT_OFFSET * throt))
+
+/* get CCROC_THROT_PSKIP_xxx offset per HIGH/MED/LOW vect*/
+#define CCROC_THROT_OFFSET                     0x0c
+#define CCROC_THROT_PSKIP_CTRL_CPU_REG(vect)    (CCROC_THROT_PSKIP_CTRL_CPU + \
+                                               (CCROC_THROT_OFFSET * vect))
+#define CCROC_THROT_PSKIP_RAMP_CPU_REG(vect)    (CCROC_THROT_PSKIP_RAMP_CPU + \
+                                               (CCROC_THROT_OFFSET * vect))
+
+/* get THERMCTL_LEVELx offset per CPU/GPU/MEM/TSENSE rg and LEVEL0~3 lv */
+#define THERMCTL_LVL_REGS_SIZE         0x20
+#define THERMCTL_LVL_REG(rg, lv)       ((rg) + ((lv) * THERMCTL_LVL_REGS_SIZE))
+
 static const int min_low_temp = -127000;
 static const int max_high_temp = 127000;
 
+enum soctherm_throttle_id {
+       THROTTLE_LIGHT = 0,
+       THROTTLE_HEAVY,
+       THROTTLE_SIZE,
+};
+
+enum soctherm_throttle_dev_id {
+       THROTTLE_DEV_CPU = 0,
+       THROTTLE_DEV_GPU,
+       THROTTLE_DEV_SIZE,
+};
+
+static const char *const throt_names[] = {
+       [THROTTLE_LIGHT] = "light",
+       [THROTTLE_HEAVY] = "heavy",
+};
+
+struct tegra_soctherm;
 struct tegra_thermctl_zone {
        void __iomem *reg;
        struct device *dev;
+       struct tegra_soctherm *ts;
        struct thermal_zone_device *tz;
        const struct tegra_tsensor_group *sg;
 };
 
+struct soctherm_throt_cfg {
+       const char *name;
+       unsigned int id;
+       u8 priority;
+       u8 cpu_throt_level;
+       u32 cpu_throt_depth;
+       struct thermal_cooling_device *cdev;
+       bool init;
+};
+
 struct tegra_soctherm {
        struct reset_control *reset;
        struct clk *clock_tsensor;
        struct clk *clock_soctherm;
        void __iomem *regs;
-       struct thermal_zone_device **thermctl_tzs;
+       void __iomem *clk_regs;
+       void __iomem *ccroc_regs;
 
        u32 *calib;
+       struct thermal_zone_device **thermctl_tzs;
        struct tegra_soctherm_soc *soc;
 
+       struct soctherm_throt_cfg throt_cfgs[THROTTLE_SIZE];
+
        struct dentry *debugfs_dir;
 };
 
+/**
+ * clk_writel() - writes a value to a CAR register
+ * @ts: pointer to a struct tegra_soctherm
+ * @v: the value to write
+ * @reg: the register offset
+ *
+ * Writes @v to @reg.  No return value.
+ */
+static inline void clk_writel(struct tegra_soctherm *ts, u32 value, u32 reg)
+{
+       writel(value, (ts->clk_regs + reg));
+}
+
+/**
+ * clk_readl() - reads specified register from CAR IP block
+ * @ts: pointer to a struct tegra_soctherm
+ * @reg: register address to be read
+ *
+ * Return: the value of the register
+ */
+static inline u32 clk_readl(struct tegra_soctherm *ts, u32 reg)
+{
+       return readl(ts->clk_regs + reg);
+}
+
+/**
+ * ccroc_writel() - writes a value to a CCROC register
+ * @ts: pointer to a struct tegra_soctherm
+ * @v: the value to write
+ * @reg: the register offset
+ *
+ * Writes @v to @reg.  No return value.
+ */
+static inline void ccroc_writel(struct tegra_soctherm *ts, u32 value, u32 reg)
+{
+       writel(value, (ts->ccroc_regs + reg));
+}
+
+/**
+ * ccroc_readl() - reads specified register from CCROC IP block
+ * @ts: pointer to a struct tegra_soctherm
+ * @reg: register address to be read
+ *
+ * Return: the value of the register
+ */
+static inline u32 ccroc_readl(struct tegra_soctherm *ts, u32 reg)
+{
+       return readl(ts->ccroc_regs + reg);
+}
+
 static void enable_tsensor(struct tegra_soctherm *tegra, unsigned int i)
 {
        const struct tegra_tsensor *sensor = &tegra->soc->tsensors[i];
@@ -150,11 +344,17 @@ static int tegra_thermctl_get_temp(void *data, int *out_temp)
 static int
 thermtrip_program(struct device *dev, const struct tegra_tsensor_group *sg,
                  int trip_temp);
+static int
+throttrip_program(struct device *dev, const struct tegra_tsensor_group *sg,
+                 struct soctherm_throt_cfg *stc, int trip_temp);
+static struct soctherm_throt_cfg *
+find_throttle_cfg_by_name(struct tegra_soctherm *ts, const char *name);
 
 static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp)
 {
        struct tegra_thermctl_zone *zone = data;
        struct thermal_zone_device *tz = zone->tz;
+       struct tegra_soctherm *ts = zone->ts;
        const struct tegra_tsensor_group *sg = zone->sg;
        struct device *dev = zone->dev;
        enum thermal_trip_type type;
@@ -167,10 +367,29 @@ static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp)
        if (ret)
                return ret;
 
-       if (type != THERMAL_TRIP_CRITICAL)
-               return 0;
+       if (type == THERMAL_TRIP_CRITICAL) {
+               return thermtrip_program(dev, sg, temp);
+       } else if (type == THERMAL_TRIP_HOT) {
+               int i;
+
+               for (i = 0; i < THROTTLE_SIZE; i++) {
+                       struct thermal_cooling_device *cdev;
+                       struct soctherm_throt_cfg *stc;
+
+                       if (!ts->throt_cfgs[i].init)
+                               continue;
+
+                       cdev = ts->throt_cfgs[i].cdev;
+                       if (get_thermal_instance(tz, cdev, trip))
+                               stc = find_throttle_cfg_by_name(ts, cdev->type);
+                       else
+                               continue;
+
+                       return throttrip_program(dev, sg, stc, temp);
+               }
+       }
 
-       return thermtrip_program(dev, sg, temp);
+       return 0;
 }
 
 static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = {
@@ -237,15 +456,111 @@ static int thermtrip_program(struct device *dev,
        return 0;
 }
 
+/**
+ * throttrip_program() - Configures the hardware to throttle the
+ * pulse if a given sensor group reaches a given temperature
+ * @dev: ptr to the struct device for the SOC_THERM IP block
+ * @sg: pointer to the sensor group to set the thermtrip temperature for
+ * @stc: pointer to the throttle need to be triggered
+ * @trip_temp: the temperature in millicelsius to trigger the thermal trip at
+ *
+ * Sets the thermal trip threshold and throttle event of the given sensor
+ * group. If this threshold is crossed, the hardware will trigger the
+ * throttle.
+ *
+ * Note that, although @trip_temp is specified in millicelsius, the
+ * hardware is programmed in degrees Celsius.
+ *
+ * Return: 0 upon success, or %-EINVAL upon failure.
+ */
+static int throttrip_program(struct device *dev,
+                            const struct tegra_tsensor_group *sg,
+                            struct soctherm_throt_cfg *stc,
+                            int trip_temp)
+{
+       struct tegra_soctherm *ts = dev_get_drvdata(dev);
+       int temp, cpu_throt, gpu_throt;
+       unsigned int throt;
+       u32 r, reg_off;
+
+       if (!dev || !sg || !stc || !stc->init)
+               return -EINVAL;
+
+       temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain;
+
+       /* Hardcode LIGHT on LEVEL1 and HEAVY on LEVEL2 */
+       throt = stc->id;
+       reg_off = THERMCTL_LVL_REG(sg->thermctl_lvl0_offset, throt + 1);
+
+       if (throt == THROTTLE_LIGHT) {
+               cpu_throt = THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT;
+               gpu_throt = THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT;
+       } else {
+               cpu_throt = THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY;
+               gpu_throt = THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY;
+               if (throt != THROTTLE_HEAVY)
+                       dev_warn(dev,
+                                "invalid throt id %d - assuming HEAVY",
+                                throt);
+       }
+
+       r = readl(ts->regs + reg_off);
+       r = REG_SET_MASK(r, sg->thermctl_lvl0_up_thresh_mask, temp);
+       r = REG_SET_MASK(r, sg->thermctl_lvl0_dn_thresh_mask, temp);
+       r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_CPU_THROT_MASK, cpu_throt);
+       r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_GPU_THROT_MASK, gpu_throt);
+       r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 1);
+       writel(r, ts->regs + reg_off);
+
+       return 0;
+}
+
+static struct soctherm_throt_cfg *
+find_throttle_cfg_by_name(struct tegra_soctherm *ts, const char *name)
+{
+       unsigned int i;
+
+       for (i = 0; ts->throt_cfgs[i].name; i++)
+               if (!strcmp(ts->throt_cfgs[i].name, name))
+                       return &ts->throt_cfgs[i];
+
+       return NULL;
+}
+
+static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp)
+{
+       int ntrips, i, ret;
+       enum thermal_trip_type type;
+
+       ntrips = of_thermal_get_ntrips(tz);
+       if (ntrips <= 0)
+               return -EINVAL;
+
+       for (i = 0; i < ntrips; i++) {
+               ret = tz->ops->get_trip_type(tz, i, &type);
+               if (ret)
+                       return -EINVAL;
+               if (type == THERMAL_TRIP_HOT) {
+                       ret = tz->ops->get_trip_temp(tz, i, temp);
+                       if (!ret)
+                               *trip = i;
+
+                       return ret;
+               }
+       }
+
+       return -EINVAL;
+}
+
 /**
  * tegra_soctherm_set_hwtrips() - set HW trip point from DT data
  * @dev: struct device * of the SOC_THERM instance
  *
  * Configure the SOC_THERM HW trip points, setting "THERMTRIP"
- * trip points , using "critical" type trip_temp from thermal
- * zone.
- * After they have been configured, THERMTRIP will take action
- * when the configured SoC thermal sensor group reaches a
+ * "THROTTLE" trip points , using "critical" or "hot" type trip_temp
+ * from thermal zone.
+ * After they have been configured, THERMTRIP or THROTTLE will take
+ * action when the configured SoC thermal sensor group reaches a
  * certain temperature.
  *
  * Return: 0 upon success, or a negative error code on failure.
@@ -254,19 +569,24 @@ static int thermtrip_program(struct device *dev,
  * THERMTRIP has been enabled successfully when a message similar to
  * this one appears on the serial console:
  * "thermtrip: will shut down when sensor group XXX reaches YYYYYY mC"
+ * THROTTLE has been enabled successfully when a message similar to
+ * this one appears on the serial console:
+ * ""throttrip: will throttle when sensor group XXX reaches YYYYYY mC"
  */
 static int tegra_soctherm_set_hwtrips(struct device *dev,
                                      const struct tegra_tsensor_group *sg,
                                      struct thermal_zone_device *tz)
 {
-       int temperature;
+       struct tegra_soctherm *ts = dev_get_drvdata(dev);
+       struct soctherm_throt_cfg *stc;
+       int i, trip, temperature;
        int ret;
 
        ret = tz->ops->get_crit_temp(tz, &temperature);
        if (ret) {
                dev_warn(dev, "thermtrip: %s: missing critical temperature\n",
                         sg->name);
-               return ret;
+               goto set_throttle;
        }
 
        ret = thermtrip_program(dev, sg, temperature);
@@ -280,6 +600,43 @@ static int tegra_soctherm_set_hwtrips(struct device *dev,
                 "thermtrip: will shut down when %s reaches %d mC\n",
                 sg->name, temperature);
 
+set_throttle:
+       ret = get_hot_temp(tz, &trip, &temperature);
+       if (ret) {
+               dev_warn(dev, "throttrip: %s: missing hot temperature\n",
+                        sg->name);
+               return 0;
+       }
+
+       for (i = 0; i < THROTTLE_SIZE; i++) {
+               struct thermal_cooling_device *cdev;
+
+               if (!ts->throt_cfgs[i].init)
+                       continue;
+
+               cdev = ts->throt_cfgs[i].cdev;
+               if (get_thermal_instance(tz, cdev, trip))
+                       stc = find_throttle_cfg_by_name(ts, cdev->type);
+               else
+                       continue;
+
+               ret = throttrip_program(dev, sg, stc, temperature);
+               if (ret) {
+                       dev_err(dev, "throttrip: %s: error during enable\n",
+                               sg->name);
+                       return ret;
+               }
+
+               dev_info(dev,
+                        "throttrip: will throttle when %s reaches %d mC\n",
+                        sg->name, temperature);
+               break;
+       }
+
+       if (i == THROTTLE_SIZE)
+               dev_warn(dev, "throttrip: %s: missing throttle cdev\n",
+                        sg->name);
+
        return 0;
 }
 
@@ -291,7 +648,7 @@ static int regs_show(struct seq_file *s, void *data)
        const struct tegra_tsensor *tsensors = ts->soc->tsensors;
        const struct tegra_tsensor_group **ttgs = ts->soc->ttgs;
        u32 r, state;
-       int i;
+       int i, level;
 
        seq_puts(s, "-----TSENSE (convert HW)-----\n");
 
@@ -365,6 +722,81 @@ static int regs_show(struct seq_file *s, void *data)
        state = REG_GET_MASK(r, SENSOR_TEMP2_MEM_TEMP_MASK);
        seq_printf(s, " MEM(%d)\n", translate_temp(state));
 
+       for (i = 0; i < ts->soc->num_ttgs; i++) {
+               seq_printf(s, "%s:\n", ttgs[i]->name);
+               for (level = 0; level < 4; level++) {
+                       s32 v;
+                       u32 mask;
+                       u16 off = ttgs[i]->thermctl_lvl0_offset;
+
+                       r = readl(ts->regs + THERMCTL_LVL_REG(off, level));
+
+                       mask = ttgs[i]->thermctl_lvl0_up_thresh_mask;
+                       state = REG_GET_MASK(r, mask);
+                       v = sign_extend32(state, ts->soc->bptt - 1);
+                       v *= ts->soc->thresh_grain;
+                       seq_printf(s, "   %d: Up/Dn(%d /", level, v);
+
+                       mask = ttgs[i]->thermctl_lvl0_dn_thresh_mask;
+                       state = REG_GET_MASK(r, mask);
+                       v = sign_extend32(state, ts->soc->bptt - 1);
+                       v *= ts->soc->thresh_grain;
+                       seq_printf(s, "%d ) ", v);
+
+                       mask = THERMCTL_LVL0_CPU0_EN_MASK;
+                       state = REG_GET_MASK(r, mask);
+                       seq_printf(s, "En(%d) ", state);
+
+                       mask = THERMCTL_LVL0_CPU0_CPU_THROT_MASK;
+                       state = REG_GET_MASK(r, mask);
+                       seq_puts(s, "CPU Throt");
+                       if (!state)
+                               seq_printf(s, "(%s) ", "none");
+                       else if (state == THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT)
+                               seq_printf(s, "(%s) ", "L");
+                       else if (state == THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY)
+                               seq_printf(s, "(%s) ", "H");
+                       else
+                               seq_printf(s, "(%s) ", "H+L");
+
+                       mask = THERMCTL_LVL0_CPU0_GPU_THROT_MASK;
+                       state = REG_GET_MASK(r, mask);
+                       seq_puts(s, "GPU Throt");
+                       if (!state)
+                               seq_printf(s, "(%s) ", "none");
+                       else if (state == THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT)
+                               seq_printf(s, "(%s) ", "L");
+                       else if (state == THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY)
+                               seq_printf(s, "(%s) ", "H");
+                       else
+                               seq_printf(s, "(%s) ", "H+L");
+
+                       mask = THERMCTL_LVL0_CPU0_STATUS_MASK;
+                       state = REG_GET_MASK(r, mask);
+                       seq_printf(s, "Status(%s)\n",
+                                  state == 0 ? "LO" :
+                                  state == 1 ? "In" :
+                                  state == 2 ? "Res" : "HI");
+               }
+       }
+
+       r = readl(ts->regs + THERMCTL_STATS_CTL);
+       seq_printf(s, "STATS: Up(%s) Dn(%s)\n",
+                  r & STATS_CTL_EN_UP ? "En" : "--",
+                  r & STATS_CTL_EN_DN ? "En" : "--");
+
+       for (level = 0; level < 4; level++) {
+               u16 off;
+
+               off = THERMCTL_LVL0_UP_STATS;
+               r = readl(ts->regs + THERMCTL_LVL_REG(off, level));
+               seq_printf(s, "  Level_%d Up(%d) ", level, r);
+
+               off = THERMCTL_LVL0_DN_STATS;
+               r = readl(ts->regs + THERMCTL_LVL_REG(off, level));
+               seq_printf(s, "Dn(%d)\n", r);
+       }
+
        r = readl(ts->regs + THERMCTL_THERMTRIP_CTL);
        state = REG_GET_MASK(r, ttgs[0]->thermtrip_any_en_mask);
        seq_printf(s, "Thermtrip Any En(%d)\n", state);
@@ -376,6 +808,32 @@ static int regs_show(struct seq_file *s, void *data)
                seq_printf(s, "Thresh(%d)\n", state);
        }
 
+       r = readl(ts->regs + THROT_GLOBAL_CFG);
+       seq_puts(s, "\n");
+       seq_printf(s, "GLOBAL THROTTLE CONFIG: 0x%08x\n", r);
+
+       seq_puts(s, "---------------------------------------------------\n");
+       r = readl(ts->regs + THROT_STATUS);
+       state = REG_GET_MASK(r, THROT_STATUS_BREACH_MASK);
+       seq_printf(s, "THROT STATUS: breach(%d) ", state);
+       state = REG_GET_MASK(r, THROT_STATUS_STATE_MASK);
+       seq_printf(s, "state(%d) ", state);
+       state = REG_GET_MASK(r, THROT_STATUS_ENABLED_MASK);
+       seq_printf(s, "enabled(%d)\n", state);
+
+       r = readl(ts->regs + CPU_PSKIP_STATUS);
+       if (ts->soc->use_ccroc) {
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK);
+               seq_printf(s, "CPU PSKIP STATUS: enabled(%d)\n", state);
+       } else {
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_M_MASK);
+               seq_printf(s, "CPU PSKIP STATUS: M(%d) ", state);
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_N_MASK);
+               seq_printf(s, "N(%d) ", state);
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK);
+               seq_printf(s, "enabled(%d)\n", state);
+       }
+
        return 0;
 }
 
@@ -449,6 +907,326 @@ static int soctherm_clk_enable(struct platform_device *pdev, bool enable)
        return 0;
 }
 
+static int throt_get_cdev_max_state(struct thermal_cooling_device *cdev,
+                                   unsigned long *max_state)
+{
+       *max_state = 1;
+       return 0;
+}
+
+static int throt_get_cdev_cur_state(struct thermal_cooling_device *cdev,
+                                   unsigned long *cur_state)
+{
+       struct tegra_soctherm *ts = cdev->devdata;
+       u32 r;
+
+       r = readl(ts->regs + THROT_STATUS);
+       if (REG_GET_MASK(r, THROT_STATUS_STATE_MASK))
+               *cur_state = 1;
+       else
+               *cur_state = 0;
+
+       return 0;
+}
+
+static int throt_set_cdev_state(struct thermal_cooling_device *cdev,
+                               unsigned long cur_state)
+{
+       return 0;
+}
+
+static struct thermal_cooling_device_ops throt_cooling_ops = {
+       .get_max_state = throt_get_cdev_max_state,
+       .get_cur_state = throt_get_cdev_cur_state,
+       .set_cur_state = throt_set_cdev_state,
+};
+
+/**
+ * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations
+ * and register them as cooling devices.
+ */
+static void soctherm_init_hw_throt_cdev(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct tegra_soctherm *ts = dev_get_drvdata(dev);
+       struct device_node *np_stc, *np_stcc;
+       const char *name;
+       u32 val;
+       int i, r;
+
+       for (i = 0; i < THROTTLE_SIZE; i++) {
+               ts->throt_cfgs[i].name = throt_names[i];
+               ts->throt_cfgs[i].id = i;
+               ts->throt_cfgs[i].init = false;
+       }
+
+       np_stc = of_get_child_by_name(dev->of_node, "throttle-cfgs");
+       if (!np_stc) {
+               dev_info(dev,
+                        "throttle-cfg: no throttle-cfgs - not enabling\n");
+               return;
+       }
+
+       for_each_child_of_node(np_stc, np_stcc) {
+               struct soctherm_throt_cfg *stc;
+               struct thermal_cooling_device *tcd;
+
+               name = np_stcc->name;
+               stc = find_throttle_cfg_by_name(ts, name);
+               if (!stc) {
+                       dev_err(dev,
+                               "throttle-cfg: could not find %s\n", name);
+                       continue;
+               }
+
+               r = of_property_read_u32(np_stcc, "nvidia,priority", &val);
+               if (r) {
+                       dev_info(dev,
+                                "throttle-cfg: %s: missing priority\n", name);
+                       continue;
+               }
+               stc->priority = val;
+
+               if (ts->soc->use_ccroc) {
+                       r = of_property_read_u32(np_stcc,
+                                                "nvidia,cpu-throt-level",
+                                                &val);
+                       if (r) {
+                               dev_info(dev,
+                                        "throttle-cfg: %s: missing cpu-throt-level\n",
+                                        name);
+                               continue;
+                       }
+                       stc->cpu_throt_level = val;
+               } else {
+                       r = of_property_read_u32(np_stcc,
+                                                "nvidia,cpu-throt-percent",
+                                                &val);
+                       if (r) {
+                               dev_info(dev,
+                                        "throttle-cfg: %s: missing cpu-throt-percent\n",
+                                        name);
+                               continue;
+                       }
+                       stc->cpu_throt_depth = val;
+               }
+
+               tcd = thermal_of_cooling_device_register(np_stcc,
+                                                        (char *)name, ts,
+                                                        &throt_cooling_ops);
+               of_node_put(np_stcc);
+               if (IS_ERR_OR_NULL(tcd)) {
+                       dev_err(dev,
+                               "throttle-cfg: %s: failed to register cooling device\n",
+                               name);
+                       continue;
+               }
+
+               stc->cdev = tcd;
+               stc->init = true;
+       }
+
+       of_node_put(np_stc);
+}
+
+/**
+ * throttlectl_cpu_level_cfg() - programs CCROC NV_THERM level config
+ * @level: describing the level LOW/MED/HIGH of throttling
+ *
+ * It's necessary to set up the CPU-local CCROC NV_THERM instance with
+ * the M/N values desired for each level. This function does this.
+ *
+ * This function pre-programs the CCROC NV_THERM levels in terms of
+ * pre-configured "Low", "Medium" or "Heavy" throttle levels which are
+ * mapped to THROT_LEVEL_LOW, THROT_LEVEL_MED and THROT_LEVEL_HVY.
+ */
+static void throttlectl_cpu_level_cfg(struct tegra_soctherm *ts, int level)
+{
+       u8 depth, dividend;
+       u32 r;
+
+       switch (level) {
+       case TEGRA_SOCTHERM_THROT_LEVEL_LOW:
+               depth = 50;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_MED:
+               depth = 75;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_HIGH:
+               depth = 80;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_NONE:
+               return;
+       default:
+               return;
+       }
+
+       dividend = THROT_DEPTH_DIVIDEND(depth);
+
+       /* setup PSKIP in ccroc nv_therm registers */
+       r = ccroc_readl(ts, CCROC_THROT_PSKIP_RAMP_CPU_REG(level));
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_DURATION_MASK, 0xff);
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_STEP_MASK, 0xf);
+       ccroc_writel(ts, r, CCROC_THROT_PSKIP_RAMP_CPU_REG(level));
+
+       r = ccroc_readl(ts, CCROC_THROT_PSKIP_CTRL_CPU_REG(level));
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_ENB_MASK, 1);
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend);
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff);
+       ccroc_writel(ts, r, CCROC_THROT_PSKIP_CTRL_CPU_REG(level));
+}
+
+/**
+ * throttlectl_cpu_level_select() - program CPU pulse skipper config
+ * @throt: the LIGHT/HEAVY of throttle event id
+ *
+ * Pulse skippers are used to throttle clock frequencies.  This
+ * function programs the pulse skippers based on @throt and platform
+ * data.  This function is used on SoCs which have CPU-local pulse
+ * skipper control, such as T13x. It programs soctherm's interface to
+ * Denver:CCROC NV_THERM in terms of Low, Medium and HIGH throttling
+ * vectors. PSKIP_BYPASS mode is set as required per HW spec.
+ */
+static void throttlectl_cpu_level_select(struct tegra_soctherm *ts,
+                                        enum soctherm_throttle_id throt)
+{
+       u32 r, throt_vect;
+
+       /* Denver:CCROC NV_THERM interface N:3 Mapping */
+       switch (ts->throt_cfgs[throt].cpu_throt_level) {
+       case TEGRA_SOCTHERM_THROT_LEVEL_LOW:
+               throt_vect = THROT_VECT_LOW;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_MED:
+               throt_vect = THROT_VECT_MED;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_HIGH:
+               throt_vect = THROT_VECT_HIGH;
+               break;
+       default:
+               throt_vect = THROT_VECT_NONE;
+               break;
+       }
+
+       r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU));
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1);
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_CPU_MASK, throt_vect);
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT2_CPU_MASK, throt_vect);
+       writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU));
+
+       /* bypass sequencer in soc_therm as it is programmed in ccroc */
+       r = REG_SET_MASK(0, THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK, 1);
+       writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU));
+}
+
+/**
+ * throttlectl_cpu_mn() - program CPU pulse skipper configuration
+ * @throt: the LIGHT/HEAVY of throttle event id
+ *
+ * Pulse skippers are used to throttle clock frequencies.  This
+ * function programs the pulse skippers based on @throt and platform
+ * data.  This function is used for CPUs that have "remote" pulse
+ * skipper control, e.g., the CPU pulse skipper is controlled by the
+ * SOC_THERM IP block.  (SOC_THERM is located outside the CPU
+ * complex.)
+ */
+static void throttlectl_cpu_mn(struct tegra_soctherm *ts,
+                              enum soctherm_throttle_id throt)
+{
+       u32 r;
+       int depth;
+       u8 dividend;
+
+       depth = ts->throt_cfgs[throt].cpu_throt_depth;
+       dividend = THROT_DEPTH_DIVIDEND(depth);
+
+       r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU));
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1);
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend);
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff);
+       writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU));
+
+       r = readl(ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU));
+       r = REG_SET_MASK(r, THROT_PSKIP_RAMP_DURATION_MASK, 0xff);
+       r = REG_SET_MASK(r, THROT_PSKIP_RAMP_STEP_MASK, 0xf);
+       writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU));
+}
+
+/**
+ * soctherm_throttle_program() - programs pulse skippers' configuration
+ * @throt: the LIGHT/HEAVY of the throttle event id.
+ *
+ * Pulse skippers are used to throttle clock frequencies.
+ * This function programs the pulse skippers.
+ */
+static void soctherm_throttle_program(struct tegra_soctherm *ts,
+                                     enum soctherm_throttle_id throt)
+{
+       u32 r;
+       struct soctherm_throt_cfg stc = ts->throt_cfgs[throt];
+
+       if (!stc.init)
+               return;
+
+       /* Setup PSKIP parameters */
+       if (ts->soc->use_ccroc)
+               throttlectl_cpu_level_select(ts, throt);
+       else
+               throttlectl_cpu_mn(ts, throt);
+
+       r = REG_SET_MASK(0, THROT_PRIORITY_LITE_PRIO_MASK, stc.priority);
+       writel(r, ts->regs + THROT_PRIORITY_CTRL(throt));
+
+       r = REG_SET_MASK(0, THROT_DELAY_LITE_DELAY_MASK, 0);
+       writel(r, ts->regs + THROT_DELAY_CTRL(throt));
+
+       r = readl(ts->regs + THROT_PRIORITY_LOCK);
+       r = REG_GET_MASK(r, THROT_PRIORITY_LOCK_PRIORITY_MASK);
+       if (r >= stc.priority)
+               return;
+       r = REG_SET_MASK(0, THROT_PRIORITY_LOCK_PRIORITY_MASK,
+                        stc.priority);
+       writel(r, ts->regs + THROT_PRIORITY_LOCK);
+}
+
+static void tegra_soctherm_throttle(struct device *dev)
+{
+       struct tegra_soctherm *ts = dev_get_drvdata(dev);
+       u32 v;
+       int i;
+
+       /* configure LOW, MED and HIGH levels for CCROC NV_THERM */
+       if (ts->soc->use_ccroc) {
+               throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_LOW);
+               throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_MED);
+               throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_HIGH);
+       }
+
+       /* Thermal HW throttle programming */
+       for (i = 0; i < THROTTLE_SIZE; i++)
+               soctherm_throttle_program(ts, i);
+
+       v = REG_SET_MASK(0, THROT_GLOBAL_ENB_MASK, 1);
+       if (ts->soc->use_ccroc) {
+               ccroc_writel(ts, v, CCROC_GLOBAL_CFG);
+
+               v = ccroc_readl(ts, CCROC_SUPER_CCLKG_DIVIDER);
+               v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1);
+               ccroc_writel(ts, v, CCROC_SUPER_CCLKG_DIVIDER);
+       } else {
+               writel(v, ts->regs + THROT_GLOBAL_CFG);
+
+               v = clk_readl(ts, CAR_SUPER_CCLKG_DIVIDER);
+               v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1);
+               clk_writel(ts, v, CAR_SUPER_CCLKG_DIVIDER);
+       }
+
+       /* initialize stats collection */
+       v = STATS_CTL_CLR_DN | STATS_CTL_EN_DN |
+           STATS_CTL_CLR_UP | STATS_CTL_EN_UP;
+       writel(v, ts->regs + THERMCTL_STATS_CTL);
+}
+
 static void soctherm_init(struct platform_device *pdev)
 {
        struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
@@ -475,6 +1253,9 @@ static void soctherm_init(struct platform_device *pdev)
        }
        writel(pdiv, tegra->regs + SENSOR_PDIV);
        writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF);
+
+       /* Configure hw throttle */
+       tegra_soctherm_throttle(&pdev->dev);
 }
 
 static const struct of_device_id tegra_soctherm_of_match[] = {
@@ -527,10 +1308,31 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
 
        tegra->soc = soc;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                          "soctherm-reg");
        tegra->regs = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(tegra->regs))
+       if (IS_ERR(tegra->regs)) {
+               dev_err(&pdev->dev, "can't get soctherm registers");
                return PTR_ERR(tegra->regs);
+       }
+
+       if (!tegra->soc->use_ccroc) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  "car-reg");
+               tegra->clk_regs = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(tegra->clk_regs)) {
+                       dev_err(&pdev->dev, "can't get car clk registers");
+                       return PTR_ERR(tegra->clk_regs);
+               }
+       } else {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  "ccroc-reg");
+               tegra->ccroc_regs = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(tegra->ccroc_regs)) {
+                       dev_err(&pdev->dev, "can't get ccroc registers");
+                       return PTR_ERR(tegra->ccroc_regs);
+               }
+       }
 
        tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm");
        if (IS_ERR(tegra->reset)) {
@@ -580,6 +1382,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
        if (err)
                return err;
 
+       soctherm_init_hw_throt_cdev(pdev);
+
        soctherm_init(pdev);
 
        for (i = 0; i < soc->num_ttgs; ++i) {
@@ -593,6 +1397,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
                zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset;
                zone->dev = &pdev->dev;
                zone->sg = soc->ttgs[i];
+               zone->ts = tegra;
 
                z = devm_thermal_zone_of_sensor_register(&pdev->dev,
                                                         soc->ttgs[i]->id, zone,
@@ -608,7 +1413,9 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
                tegra->thermctl_tzs[soc->ttgs[i]->id] = z;
 
                /* Configure hw trip points */
-               tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z);
+               err = tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z);
+               if (err)
+                       goto disable_clocks;
        }
 
        soctherm_debug_init(pdev);
@@ -661,7 +1468,12 @@ static int __maybe_unused soctherm_resume(struct device *dev)
                struct thermal_zone_device *tz;
 
                tz = tegra->thermctl_tzs[soc->ttgs[i]->id];
-               tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz);
+               err = tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz);
+               if (err) {
+                       dev_err(&pdev->dev,
+                               "Resume failed: set hwtrips failed\n");
+                       return err;
+               }
        }
 
        return 0;
index 28e18ec4b4c3c8c7640f63172ee420fad088efee..e96ca73fd780bd0a47918df573312ba8ee97def6 100644 (file)
 #ifndef __DRIVERS_THERMAL_TEGRA_SOCTHERM_H
 #define __DRIVERS_THERMAL_TEGRA_SOCTHERM_H
 
+#define THERMCTL_LEVEL0_GROUP_CPU               0x0
+#define THERMCTL_LEVEL0_GROUP_GPU              0x4
+#define THERMCTL_LEVEL0_GROUP_MEM              0x8
+#define THERMCTL_LEVEL0_GROUP_TSENSE           0xc
+
 #define SENSOR_CONFIG2                          8
 #define SENSOR_CONFIG2_THERMA_MASK             (0xffff << 16)
 #define SENSOR_CONFIG2_THERMA_SHIFT            16
@@ -65,6 +70,9 @@ struct tegra_tsensor_group {
        u32 thermtrip_enable_mask;
        u32 thermtrip_any_en_mask;
        u32 thermtrip_threshold_mask;
+       u16 thermctl_lvl0_offset;
+       u32 thermctl_lvl0_up_thresh_mask;
+       u32 thermctl_lvl0_dn_thresh_mask;
 };
 
 struct tegra_tsensor_configuration {
@@ -103,6 +111,8 @@ struct tegra_soctherm_soc {
        const unsigned int num_ttgs;
        const struct tegra_soctherm_fuse *tfuse;
        const int thresh_grain;
+       const unsigned int bptt;
+       const bool use_ccroc;
 };
 
 int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
index beb9d36b9c8ab0ffe8be0a2916c0b4dc7be1f72d..36768630f78c9f435a8961d74a3125dd6dec2a1f 100644 (file)
 #define TEGRA124_THERMTRIP_CPU_THRESH_MASK     (0xff << 8)
 #define TEGRA124_THERMTRIP_TSENSE_THRESH_MASK  0xff
 
+#define TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK  (0xff << 17)
+#define TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK  (0xff << 9)
+
 #define TEGRA124_THRESH_GRAIN                  1000
+#define TEGRA124_BPTT                          8
 
 static const struct tegra_tsensor_configuration tegra124_tsensor_config = {
        .tall = 16300,
@@ -51,6 +55,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_cpu = {
        .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA124_THERMTRIP_CPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA124_THERMTRIP_CPU_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = {
@@ -66,6 +73,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = {
        .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA124_THERMTRIP_GPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra124_tsensor_group_pll = {
@@ -79,6 +89,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_pll = {
        .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA124_THERMTRIP_TSENSE_EN_MASK,
        .thermtrip_threshold_mask = TEGRA124_THERMTRIP_TSENSE_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
+       .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra124_tsensor_group_mem = {
@@ -94,6 +107,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_mem = {
        .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA124_THERMTRIP_MEM_EN_MASK,
        .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
+       .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group *tegra124_tsensor_groups[] = {
@@ -193,4 +209,6 @@ const struct tegra_soctherm_soc tegra124_soctherm = {
        .num_ttgs = ARRAY_SIZE(tegra124_tsensor_groups),
        .tfuse = &tegra124_soctherm_fuse,
        .thresh_grain = TEGRA124_THRESH_GRAIN,
+       .bptt = TEGRA124_BPTT,
+       .use_ccroc = false,
 };
index e2aa84e1b30785764f203251fc443352de3d4b65..97fa30501eb19eeec32c05c7365e4f7ab9a8af3f 100644 (file)
 #define TEGRA132_THERMTRIP_CPU_THRESH_MASK     (0xff << 8)
 #define TEGRA132_THERMTRIP_TSENSE_THRESH_MASK  0xff
 
+#define TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK  (0xff << 17)
+#define TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK  (0xff << 9)
+
 #define TEGRA132_THRESH_GRAIN                  1000
+#define TEGRA132_BPTT                          8
 
 static const struct tegra_tsensor_configuration tegra132_tsensor_config = {
        .tall = 16300,
@@ -51,6 +55,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_cpu = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_CPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_CPU_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = {
@@ -66,6 +73,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_GPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra132_tsensor_group_pll = {
@@ -79,6 +89,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_pll = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_TSENSE_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_TSENSE_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra132_tsensor_group_mem = {
@@ -94,6 +107,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_mem = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_MEM_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group *tegra132_tsensor_groups[] = {
@@ -193,4 +209,6 @@ const struct tegra_soctherm_soc tegra132_soctherm = {
        .num_ttgs = ARRAY_SIZE(tegra132_tsensor_groups),
        .tfuse = &tegra132_soctherm_fuse,
        .thresh_grain = TEGRA132_THRESH_GRAIN,
+       .bptt = TEGRA132_BPTT,
+       .use_ccroc = true,
 };
index 19cc0ab66f0eb7a5303d34e571221783d11fbbd9..ad53169a8e955718783d56a27f32cc2bf4015f28 100644 (file)
 #define TEGRA210_THERMTRIP_CPU_THRESH_MASK     (0x1ff << 9)
 #define TEGRA210_THERMTRIP_TSENSE_THRESH_MASK  0x1ff
 
+#define TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK  (0x1ff << 18)
+#define TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK  (0x1ff << 9)
+
 #define TEGRA210_THRESH_GRAIN                  500
+#define TEGRA210_BPTT                          9
 
 static const struct tegra_tsensor_configuration tegra210_tsensor_config = {
        .tall = 16300,
@@ -52,6 +56,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_cpu = {
        .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA210_THERMTRIP_CPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA210_THERMTRIP_CPU_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra210_tsensor_group_gpu = {
@@ -67,6 +74,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_gpu = {
        .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA210_THERMTRIP_GPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra210_tsensor_group_pll = {
@@ -80,6 +90,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_pll = {
        .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA210_THERMTRIP_TSENSE_EN_MASK,
        .thermtrip_threshold_mask = TEGRA210_THERMTRIP_TSENSE_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
+       .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra210_tsensor_group_mem = {
@@ -95,6 +108,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_mem = {
        .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA210_THERMTRIP_MEM_EN_MASK,
        .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
+       .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group *tegra210_tsensor_groups[] = {
@@ -194,4 +210,6 @@ const struct tegra_soctherm_soc tegra210_soctherm = {
        .num_ttgs = ARRAY_SIZE(tegra210_tsensor_groups),
        .tfuse = &tegra210_soctherm_fuse,
        .thresh_grain = TEGRA210_THRESH_GRAIN,
+       .bptt = TEGRA210_BPTT,
+       .use_ccroc = false,
 };
index e2fc6161dded9650c300cf829a381c0a2d14b38c..226b0b4aced6a2e9fd348086259fc517e3f674ee 100644 (file)
@@ -520,6 +520,56 @@ exit:
 }
 EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
 
+void thermal_zone_set_trips(struct thermal_zone_device *tz)
+{
+       int low = -INT_MAX;
+       int high = INT_MAX;
+       int trip_temp, hysteresis;
+       int i, ret;
+
+       mutex_lock(&tz->lock);
+
+       if (!tz->ops->set_trips || !tz->ops->get_trip_hyst)
+               goto exit;
+
+       for (i = 0; i < tz->trips; i++) {
+               int trip_low;
+
+               tz->ops->get_trip_temp(tz, i, &trip_temp);
+               tz->ops->get_trip_hyst(tz, i, &hysteresis);
+
+               trip_low = trip_temp - hysteresis;
+
+               if (trip_low < tz->temperature && trip_low > low)
+                       low = trip_low;
+
+               if (trip_temp > tz->temperature && trip_temp < high)
+                       high = trip_temp;
+       }
+
+       /* No need to change trip points */
+       if (tz->prev_low_trip == low && tz->prev_high_trip == high)
+               goto exit;
+
+       tz->prev_low_trip = low;
+       tz->prev_high_trip = high;
+
+       dev_dbg(&tz->device,
+               "new temperature boundaries: %d < x < %d\n", low, high);
+
+       /*
+        * Set a temperature window. When this window is left the driver
+        * must inform the thermal core via thermal_zone_device_update.
+        */
+       ret = tz->ops->set_trips(tz, low, high);
+       if (ret)
+               dev_err(&tz->device, "Failed to set trips: %d\n", ret);
+
+exit:
+       mutex_unlock(&tz->lock);
+}
+EXPORT_SYMBOL_GPL(thermal_zone_set_trips);
+
 static void update_temperature(struct thermal_zone_device *tz)
 {
        int temp, ret;
@@ -557,7 +607,8 @@ static void thermal_zone_device_reset(struct thermal_zone_device *tz)
                pos->initialized = false;
 }
 
-void thermal_zone_device_update(struct thermal_zone_device *tz)
+void thermal_zone_device_update(struct thermal_zone_device *tz,
+                               enum thermal_notify_event event)
 {
        int count;
 
@@ -569,6 +620,10 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
 
        update_temperature(tz);
 
+       thermal_zone_set_trips(tz);
+
+       tz->notify_event = event;
+
        for (count = 0; count < tz->trips; count++)
                handle_thermal_trip(tz, count);
 }
@@ -579,7 +634,7 @@ static void thermal_zone_device_check(struct work_struct *work)
        struct thermal_zone_device *tz = container_of(work, struct
                                                      thermal_zone_device,
                                                      poll_queue.work);
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 }
 
 /* sys I/F for thermal zone */
@@ -703,7 +758,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
        if (ret)
                return ret;
 
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return count;
 }
@@ -754,6 +809,9 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
         */
        ret = tz->ops->set_trip_hyst(tz, trip, temperature);
 
+       if (!ret)
+               thermal_zone_set_trips(tz);
+
        return ret ? ret : count;
 }
 
@@ -822,7 +880,7 @@ passive_store(struct device *dev, struct device_attribute *attr,
 
        tz->forced_passive = state;
 
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return count;
 }
@@ -913,7 +971,7 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
        }
 
        if (!ret)
-               thermal_zone_device_update(tz);
+               thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return ret ? ret : count;
 }
@@ -1509,7 +1567,8 @@ __thermal_cooling_device_register(struct device_node *np,
        mutex_lock(&thermal_list_lock);
        list_for_each_entry(pos, &thermal_tz_list, node)
                if (atomic_cmpxchg(&pos->need_update, 1, 0))
-                       thermal_zone_device_update(pos);
+                       thermal_zone_device_update(pos,
+                                                  THERMAL_EVENT_UNSPECIFIED);
        mutex_unlock(&thermal_list_lock);
 
        return cdev;
@@ -1952,7 +2011,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
        thermal_zone_device_reset(tz);
        /* Update the new thermal zone and mark it as already updated. */
        if (atomic_cmpxchg(&tz->need_update, 1, 0))
-               thermal_zone_device_update(tz);
+               thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return tz;
 
@@ -2069,6 +2128,36 @@ exit:
 }
 EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
 
+/**
+ * thermal_zone_get_slope - return the slope attribute of the thermal zone
+ * @tz: thermal zone device with the slope attribute
+ *
+ * Return: If the thermal zone device has a slope attribute, return it, else
+ * return 1.
+ */
+int thermal_zone_get_slope(struct thermal_zone_device *tz)
+{
+       if (tz && tz->tzp)
+               return tz->tzp->slope;
+       return 1;
+}
+EXPORT_SYMBOL_GPL(thermal_zone_get_slope);
+
+/**
+ * thermal_zone_get_offset - return the offset attribute of the thermal zone
+ * @tz: thermal zone device with the offset attribute
+ *
+ * Return: If the thermal zone device has a offset attribute, return it, else
+ * return 0.
+ */
+int thermal_zone_get_offset(struct thermal_zone_device *tz)
+{
+       if (tz && tz->tzp)
+               return tz->tzp->offset;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_zone_get_offset);
+
 #ifdef CONFIG_NET
 static const struct genl_multicast_group thermal_event_mcgrps[] = {
        { .name = THERMAL_GENL_MCAST_GROUP_NAME, },
@@ -2209,7 +2298,8 @@ static int thermal_pm_notify(struct notifier_block *nb,
                atomic_set(&in_suspend, 0);
                list_for_each_entry(tz, &thermal_tz_list, node) {
                        thermal_zone_device_reset(tz);
-                       thermal_zone_device_update(tz);
+                       thermal_zone_device_update(tz,
+                                                  THERMAL_EVENT_UNSPECIFIED);
                }
                break;
        default:
index 15c0a9ac2209eab4a19e358370f53bcf3288a5a5..0586bd0f2bab676f1c780bc1bc0499919ed5f88e 100644 (file)
@@ -52,7 +52,7 @@ static void ti_thermal_work(struct work_struct *work)
        struct ti_thermal_data *data = container_of(work,
                                        struct ti_thermal_data, thermal_wq);
 
-       thermal_zone_device_update(data->ti_thermal);
+       thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);
 
        dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n",
                data->ti_thermal->type);
@@ -205,7 +205,7 @@ static int ti_thermal_set_mode(struct thermal_zone_device *thermal,
        data->mode = mode;
        ti_bandgap_write_update_interval(bgp, data->sensor_id,
                                        data->ti_thermal->polling_delay);
-       thermal_zone_device_update(data->ti_thermal);
+       thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);
        dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n",
                data->ti_thermal->polling_delay);
 
@@ -239,7 +239,7 @@ static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal,
        return 0;
 }
 
-static int __ti_thermal_get_trend(void *p, long *trend)
+static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend)
 {
        struct ti_thermal_data *data = p;
        struct ti_bandgap *bgp;
@@ -252,22 +252,6 @@ static int __ti_thermal_get_trend(void *p, long *trend)
        if (ret)
                return ret;
 
-       *trend = tr;
-
-       return 0;
-}
-
-/* Get the temperature trend callback functions for thermal zone */
-static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
-                               int trip, enum thermal_trend *trend)
-{
-       int ret;
-       long tr;
-
-       ret = __ti_thermal_get_trend(thermal->devdata, &tr);
-       if (ret)
-               return ret;
-
        if (tr > 0)
                *trend = THERMAL_TREND_RAISING;
        else if (tr < 0)
@@ -278,6 +262,13 @@ static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
        return 0;
 }
 
+/* Get the temperature trend callback functions for thermal zone */
+static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
+                               int trip, enum thermal_trend *trend)
+{
+       return __ti_thermal_get_trend(thermal->devdata, trip, trend);
+}
+
 /* Get critical temperature callback functions for thermal zone */
 static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal,
                                    int *temp)
index 10adcddc88211e9589f27b1ab6c79eaa75cb7fe7..c908150c268df049a33d0139b6c0e7bb585858d6 100644 (file)
  */
 
 #include <linux/thermal.h>
-
+#include <linux/slab.h>
 #include "thermal_core.h"
 
 /**
  * notify_user_space - Notifies user space about thermal events
  * @tz - thermal_zone_device
+ * @trip - Trip point index
  *
  * This function notifies the user space through UEvents.
  */
 static int notify_user_space(struct thermal_zone_device *tz, int trip)
 {
+       char *thermal_prop[5];
+       int i;
+
        mutex_lock(&tz->lock);
-       kobject_uevent(&tz->device.kobj, KOBJ_CHANGE);
+       thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", tz->type);
+       thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", tz->temperature);
+       thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=%d", trip);
+       thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", tz->notify_event);
+       thermal_prop[4] = NULL;
+       kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop);
+       for (i = 0; i < 4; ++i)
+               kfree(thermal_prop[i]);
        mutex_unlock(&tz->lock);
        return 0;
 }
index 97f0a2bd93edfb64bc326adf29724bf456548ad8..95f4c1bcdb4caf069df9f70dbe63ec3ae64b7908 100644 (file)
@@ -348,7 +348,8 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
        }
        if (notify) {
                pr_debug("thermal_zone_device_update\n");
-               thermal_zone_device_update(phdev->tzone);
+               thermal_zone_device_update(phdev->tzone,
+                                          THERMAL_EVENT_UNSPECIFIED);
        }
 }
 
index 1e5f529d51a21f4a17db61cf0f8b7f48d8234386..063064801ceb0e37a0f4a5b45101f02f7c1def83 100644 (file)
@@ -1308,11 +1308,6 @@ MODULE_LICENSE ("GPL");
 #define        PLATFORM_DRIVER         ehci_mv_driver
 #endif
 
-#ifdef CONFIG_MIPS_SEAD3
-#include "ehci-sead3.c"
-#define        PLATFORM_DRIVER         ehci_hcd_sead3_driver
-#endif
-
 static int __init ehci_hcd_init(void)
 {
        int retval = 0;
diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c
deleted file mode 100644 (file)
index 3d86cc2..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * MIPS CI13320A EHCI Host Controller driver
- * Based on "ehci-au1xxx.c" by K.Boge <karsten.boge@amd.com>
- *
- * Copyright (C) 2012 MIPS Technologies, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/err.h>
-#include <linux/platform_device.h>
-
-static int ehci_sead3_setup(struct usb_hcd *hcd)
-{
-       int ret;
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-
-       ehci->caps = hcd->regs + 0x100;
-
-#ifdef __BIG_ENDIAN
-       ehci->big_endian_mmio = 1;
-       ehci->big_endian_desc = 1;
-#endif
-
-       ret = ehci_setup(hcd);
-       if (ret)
-               return ret;
-
-       ehci->need_io_watchdog = 0;
-
-       /* Set burst length to 16 words. */
-       ehci_writel(ehci, 0x1010, &ehci->regs->reserved1[1]);
-
-       return ret;
-}
-
-const struct hc_driver ehci_sead3_hc_driver = {
-       .description            = hcd_name,
-       .product_desc           = "SEAD-3 EHCI",
-       .hcd_priv_size          = sizeof(struct ehci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
-
-       /*
-        * basic lifecycle operations
-        *
-        */
-       .reset                  = ehci_sead3_setup,
-       .start                  = ehci_run,
-       .stop                   = ehci_stop,
-       .shutdown               = ehci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue            = ehci_urb_enqueue,
-       .urb_dequeue            = ehci_urb_dequeue,
-       .endpoint_disable       = ehci_endpoint_disable,
-       .endpoint_reset         = ehci_endpoint_reset,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number       = ehci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data        = ehci_hub_status_data,
-       .hub_control            = ehci_hub_control,
-       .bus_suspend            = ehci_bus_suspend,
-       .bus_resume             = ehci_bus_resume,
-       .relinquish_port        = ehci_relinquish_port,
-       .port_handed_over       = ehci_port_handed_over,
-
-       .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
-};
-
-static int ehci_hcd_sead3_drv_probe(struct platform_device *pdev)
-{
-       struct usb_hcd *hcd;
-       struct resource *res;
-       int ret;
-
-       if (usb_disabled())
-               return -ENODEV;
-
-       if (pdev->resource[1].flags != IORESOURCE_IRQ) {
-               pr_debug("resource[1] is not IORESOURCE_IRQ");
-               return -ENOMEM;
-       }
-       hcd = usb_create_hcd(&ehci_sead3_hc_driver, &pdev->dev, "SEAD-3");
-       if (!hcd)
-               return -ENOMEM;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(hcd->regs)) {
-               ret = PTR_ERR(hcd->regs);
-               goto err1;
-       }
-       hcd->rsrc_start = res->start;
-       hcd->rsrc_len = resource_size(res);
-
-       /* Root hub has integrated TT. */
-       hcd->has_tt = 1;
-
-       ret = usb_add_hcd(hcd, pdev->resource[1].start,
-                         IRQF_SHARED);
-       if (ret == 0) {
-               platform_set_drvdata(pdev, hcd);
-               device_wakeup_enable(hcd->self.controller);
-               return ret;
-       }
-
-err1:
-       usb_put_hcd(hcd);
-       return ret;
-}
-
-static int ehci_hcd_sead3_drv_remove(struct platform_device *pdev)
-{
-       struct usb_hcd *hcd = platform_get_drvdata(pdev);
-
-       usb_remove_hcd(hcd);
-       usb_put_hcd(hcd);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int ehci_hcd_sead3_drv_suspend(struct device *dev)
-{
-       struct usb_hcd *hcd = dev_get_drvdata(dev);
-       bool do_wakeup = device_may_wakeup(dev);
-
-       return ehci_suspend(hcd, do_wakeup);
-}
-
-static int ehci_hcd_sead3_drv_resume(struct device *dev)
-{
-       struct usb_hcd *hcd = dev_get_drvdata(dev);
-
-       ehci_resume(hcd, false);
-       return 0;
-}
-
-static const struct dev_pm_ops sead3_ehci_pmops = {
-       .suspend        = ehci_hcd_sead3_drv_suspend,
-       .resume         = ehci_hcd_sead3_drv_resume,
-};
-
-#define SEAD3_EHCI_PMOPS (&sead3_ehci_pmops)
-
-#else
-#define SEAD3_EHCI_PMOPS NULL
-#endif
-
-static struct platform_driver ehci_hcd_sead3_driver = {
-       .probe          = ehci_hcd_sead3_drv_probe,
-       .remove         = ehci_hcd_sead3_drv_remove,
-       .shutdown       = usb_hcd_platform_shutdown,
-       .driver = {
-               .name   = "sead3-ehci",
-               .pm     = SEAD3_EHCI_PMOPS,
-       }
-};
-
-MODULE_ALIAS("platform:sead3-ehci");
index 88b008fb8a4ec9f4f4458b8b65a5e7d4ccef57e5..5d3b0db5ce0af34997a3aa748292b6bd8d48c191 100644 (file)
@@ -284,12 +284,14 @@ config FB_PM2_FIFO_DISCONNECT
 config FB_ARMCLCD
        tristate "ARM PrimeCell PL110 support"
        depends on ARM || ARM64 || COMPILE_TEST
-       depends on FB && ARM_AMBA
+       depends on FB && ARM_AMBA && HAS_IOMEM
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
        select FB_MODE_HELPERS if OF
        select VIDEOMODE_HELPERS if OF
+       select BACKLIGHT_LCD_SUPPORT if OF
+       select BACKLIGHT_CLASS_DEVICE if OF
        help
          This framebuffer device driver is for the ARM PrimeCell PL110
          Colour LCD controller.  ARM PrimeCells provide the building
@@ -305,6 +307,8 @@ config PLAT_VERSATILE_CLCD
        def_bool ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || ARCH_INTEGRATOR
        depends on ARM
        depends on FB_ARMCLCD && FB=y
+       select REGMAP
+       select MFD_SYSCON
 
 config FB_ACORN
        bool "Acorn VIDC support"
@@ -2183,7 +2187,7 @@ config FB_GOLDFISH
 
 config FB_COBALT
        tristate "Cobalt server LCD frame buffer support"
-       depends on FB && (MIPS_COBALT || MIPS_SEAD3)
+       depends on FB && MIPS_COBALT
 
 config FB_SH7760
        bool "SH7760/SH7763/SH7720/SH7721 LCDC support"
@@ -2443,7 +2447,6 @@ config FB_SIMPLE
 
 source "drivers/video/fbdev/omap/Kconfig"
 source "drivers/video/fbdev/omap2/Kconfig"
-source "drivers/video/fbdev/exynos/Kconfig"
 source "drivers/video/fbdev/mmp/Kconfig"
 
 config FB_SH_MOBILE_MERAM
index f6731867dd26dfc4318799e5204cd583a5d27670..ee8c81405a7f9af1f190c98ddcfa53666ad97b0f 100644 (file)
@@ -6,8 +6,6 @@
 
 obj-y                          += core/
 
-obj-$(CONFIG_EXYNOS_VIDEO)     += exynos/
-
 obj-$(CONFIG_FB_MACMODES)      += macmodes.o
 obj-$(CONFIG_FB_WMT_GE_ROPS)   += wmt_ge_rops.o
 
@@ -79,6 +77,7 @@ obj-$(CONFIG_FB_ATMEL)                  += atmel_lcdfb.o
 obj-$(CONFIG_FB_PVR2)             += pvr2fb.o
 obj-$(CONFIG_FB_VOODOO1)          += sstfb.o
 obj-$(CONFIG_FB_ARMCLCD)         += amba-clcd.o
+obj-$(CONFIG_ARCH_NOMADIK)       += amba-clcd-nomadik.o
 obj-$(CONFIG_PLAT_VERSATILE_CLCD) += amba-clcd-versatile.o
 obj-$(CONFIG_FB_GOLDFISH)         += goldfishfb.o
 obj-$(CONFIG_FB_68328)            += 68328fb.o
diff --git a/drivers/video/fbdev/amba-clcd-nomadik.c b/drivers/video/fbdev/amba-clcd-nomadik.c
new file mode 100644 (file)
index 0000000..0c06fca
--- /dev/null
@@ -0,0 +1,259 @@
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "amba-clcd-nomadik.h"
+
+static struct gpio_desc *grestb;
+static struct gpio_desc *scen;
+static struct gpio_desc *scl;
+static struct gpio_desc *sda;
+
+static u8 tpg110_readwrite_reg(bool write, u8 address, u8 outval)
+{
+       int i;
+       u8 inval = 0;
+
+       /* Assert SCEN */
+       gpiod_set_value_cansleep(scen, 1);
+       ndelay(150);
+       /* Hammer out the address */
+       for (i = 5; i >= 0; i--) {
+               if (address & BIT(i))
+                       gpiod_set_value_cansleep(sda, 1);
+               else
+                       gpiod_set_value_cansleep(sda, 0);
+               ndelay(150);
+               /* Send an SCL pulse */
+               gpiod_set_value_cansleep(scl, 1);
+               ndelay(160);
+               gpiod_set_value_cansleep(scl, 0);
+               ndelay(160);
+       }
+
+       if (write) {
+               /* WRITE */
+               gpiod_set_value_cansleep(sda, 0);
+       } else {
+               /* READ */
+               gpiod_set_value_cansleep(sda, 1);
+       }
+       ndelay(150);
+       /* Send an SCL pulse */
+       gpiod_set_value_cansleep(scl, 1);
+       ndelay(160);
+       gpiod_set_value_cansleep(scl, 0);
+       ndelay(160);
+
+       if (!write)
+               /* HiZ turn-around cycle */
+               gpiod_direction_input(sda);
+       ndelay(150);
+       /* Send an SCL pulse */
+       gpiod_set_value_cansleep(scl, 1);
+       ndelay(160);
+       gpiod_set_value_cansleep(scl, 0);
+       ndelay(160);
+
+       /* Hammer in/out the data */
+       for (i = 7; i >= 0; i--) {
+               int value;
+
+               if (write) {
+                       value = !!(outval & BIT(i));
+                       gpiod_set_value_cansleep(sda, value);
+               } else {
+                       value = gpiod_get_value(sda);
+                       if (value)
+                               inval |= BIT(i);
+               }
+               ndelay(150);
+               /* Send an SCL pulse */
+               gpiod_set_value_cansleep(scl, 1);
+               ndelay(160);
+               gpiod_set_value_cansleep(scl, 0);
+               ndelay(160);
+       }
+
+       gpiod_direction_output(sda, 0);
+       /* Deassert SCEN */
+       gpiod_set_value_cansleep(scen, 0);
+       /* Satisfies SCEN pulse width */
+       udelay(1);
+
+       return inval;
+}
+
+static u8 tpg110_read_reg(u8 address)
+{
+       return tpg110_readwrite_reg(false, address, 0);
+}
+
+static void tpg110_write_reg(u8 address, u8 outval)
+{
+       tpg110_readwrite_reg(true, address, outval);
+}
+
+static void tpg110_startup(struct device *dev)
+{
+       u8 val;
+
+       dev_info(dev, "TPG110 display enable\n");
+       /* De-assert the reset signal */
+       gpiod_set_value_cansleep(grestb, 0);
+       mdelay(1);
+       dev_info(dev, "de-asserted GRESTB\n");
+
+       /* Test display communication */
+       tpg110_write_reg(0x00, 0x55);
+       val = tpg110_read_reg(0x00);
+       if (val == 0x55)
+               dev_info(dev, "passed communication test\n");
+       val = tpg110_read_reg(0x01);
+       dev_info(dev, "TPG110 chip ID: %d version: %d\n",
+               val>>4, val&0x0f);
+
+       /* Show display resolution */
+       val = tpg110_read_reg(0x02);
+       val &= 7;
+       switch (val) {
+       case 0x0:
+               dev_info(dev, "IN 400x240 RGB -> OUT 800x480 RGB (dual scan)");
+               break;
+       case 0x1:
+               dev_info(dev, "IN 480x272 RGB -> OUT 800x480 RGB (dual scan)");
+               break;
+       case 0x4:
+               dev_info(dev, "480x640 RGB");
+               break;
+       case 0x5:
+               dev_info(dev, "480x272 RGB");
+               break;
+       case 0x6:
+               dev_info(dev, "640x480 RGB");
+               break;
+       case 0x7:
+               dev_info(dev, "800x480 RGB");
+               break;
+       default:
+               dev_info(dev, "ILLEGAL RESOLUTION");
+               break;
+       }
+
+       val = tpg110_read_reg(0x03);
+       dev_info(dev, "resolution is controlled by %s\n",
+               (val & BIT(7)) ? "software" : "hardware");
+}
+
+static void tpg110_enable(struct clcd_fb *fb)
+{
+       struct device *dev = &fb->dev->dev;
+       static bool startup;
+       u8 val;
+
+       if (!startup) {
+               tpg110_startup(dev);
+               startup = true;
+       }
+
+       /* Take chip out of standby */
+       val = tpg110_read_reg(0x03);
+       val |= BIT(0);
+       tpg110_write_reg(0x03, val);
+}
+
+static void tpg110_disable(struct clcd_fb *fb)
+{
+       u8 val;
+
+       dev_info(&fb->dev->dev, "TPG110 display disable\n");
+       val = tpg110_read_reg(0x03);
+       /* Put into standby */
+       val &= ~BIT(0);
+       tpg110_write_reg(0x03, val);
+}
+
+static void tpg110_init(struct device *dev, struct device_node *np,
+                       struct clcd_board *board)
+{
+       dev_info(dev, "TPG110 display init\n");
+
+       grestb = devm_get_gpiod_from_child(dev, "grestb", &np->fwnode);
+       if (IS_ERR(grestb)) {
+               dev_err(dev, "no GRESTB GPIO\n");
+               return;
+       }
+       /* This asserts the GRESTB signal, putting the display into reset */
+       gpiod_direction_output(grestb, 1);
+
+       scen = devm_get_gpiod_from_child(dev, "scen", &np->fwnode);
+       if (IS_ERR(scen)) {
+               dev_err(dev, "no SCEN GPIO\n");
+               return;
+       }
+       gpiod_direction_output(scen, 0);
+       scl = devm_get_gpiod_from_child(dev, "scl", &np->fwnode);
+       if (IS_ERR(scl)) {
+               dev_err(dev, "no SCL GPIO\n");
+               return;
+       }
+       gpiod_direction_output(scl, 0);
+       sda = devm_get_gpiod_from_child(dev, "sda", &np->fwnode);
+       if (IS_ERR(sda)) {
+               dev_err(dev, "no SDA GPIO\n");
+               return;
+       }
+       gpiod_direction_output(sda, 0);
+       board->enable = tpg110_enable;
+       board->disable = tpg110_disable;
+}
+
+int nomadik_clcd_init_panel(struct clcd_fb *fb,
+                           struct device_node *endpoint)
+{
+       struct device_node *panel;
+
+       panel = of_graph_get_remote_port_parent(endpoint);
+       if (!panel)
+               return -ENODEV;
+
+       if (of_device_is_compatible(panel, "tpo,tpg110"))
+               tpg110_init(&fb->dev->dev, panel, fb->board);
+       else
+               dev_info(&fb->dev->dev, "unknown panel\n");
+
+       /* Unknown panel, fall through */
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nomadik_clcd_init_panel);
+
+#define PMU_CTRL_OFFSET 0x0000
+#define PMU_CTRL_LCDNDIF BIT(26)
+
+int nomadik_clcd_init_board(struct amba_device *adev,
+                           struct clcd_board *board)
+{
+       struct regmap *pmu_regmap;
+
+       dev_info(&adev->dev, "Nomadik CLCD board init\n");
+       pmu_regmap =
+               syscon_regmap_lookup_by_compatible("stericsson,nomadik-pmu");
+       if (IS_ERR(pmu_regmap)) {
+               dev_err(&adev->dev, "could not find PMU syscon regmap\n");
+               return PTR_ERR(pmu_regmap);
+       }
+       regmap_update_bits(pmu_regmap,
+                          PMU_CTRL_OFFSET,
+                          PMU_CTRL_LCDNDIF,
+                          0);
+       dev_info(&adev->dev, "set PMU mux to CLCD mode\n");
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nomadik_clcd_init_board);
diff --git a/drivers/video/fbdev/amba-clcd-nomadik.h b/drivers/video/fbdev/amba-clcd-nomadik.h
new file mode 100644 (file)
index 0000000..50aa9bd
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _AMBA_CLCD_NOMADIK_H
+#define _AMBA_CLCD_NOMADIK_H
+
+#include <linux/amba/bus.h>
+
+#ifdef CONFIG_ARCH_NOMADIK
+int nomadik_clcd_init_board(struct amba_device *adev,
+                            struct clcd_board *board);
+int nomadik_clcd_init_panel(struct clcd_fb *fb,
+                           struct device_node *endpoint);
+#else
+static inline int nomadik_clcd_init_board(struct amba_device *adev,
+                                         struct clcd_board *board)
+{
+       return 0;
+}
+static inline int nomadik_clcd_init_panel(struct clcd_fb *fb,
+                                         struct device_node *endpoint)
+{
+       return 0;
+}
+#endif
+
+#endif /* inclusion guard */
index a8a22daa3f9d2a70a6fa1ca5f4e0f526e7380375..19ad8645d93cd25a9f41375bcb30a1677ae721b7 100644 (file)
@@ -3,6 +3,12 @@
 #include <linux/amba/bus.h>
 #include <linux/amba/clcd.h>
 #include <linux/platform_data/video-clcd-versatile.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/bitops.h>
+#include "amba-clcd-versatile.h"
 
 static struct clcd_panel vga = {
        .mode           = {
@@ -178,3 +184,392 @@ void versatile_clcd_remove_dma(struct clcd_fb *fb)
        dma_free_wc(&fb->dev->dev, fb->fb.fix.smem_len, fb->fb.screen_base,
                    fb->fb.fix.smem_start);
 }
+
+#ifdef CONFIG_OF
+
+static struct regmap *versatile_syscon_map;
+static struct regmap *versatile_ib2_map;
+
+/*
+ * We detect the different syscon types from the compatible strings.
+ */
+enum versatile_clcd {
+       INTEGRATOR_CLCD_CM,
+       VERSATILE_CLCD,
+       REALVIEW_CLCD_EB,
+       REALVIEW_CLCD_PB1176,
+       REALVIEW_CLCD_PB11MP,
+       REALVIEW_CLCD_PBA8,
+       REALVIEW_CLCD_PBX,
+};
+
+static const struct of_device_id versatile_clcd_of_match[] = {
+       {
+               .compatible = "arm,core-module-integrator",
+               .data = (void *)INTEGRATOR_CLCD_CM,
+       },
+       {
+               .compatible = "arm,versatile-sysreg",
+               .data = (void *)VERSATILE_CLCD,
+       },
+       {
+               .compatible = "arm,realview-eb-syscon",
+               .data = (void *)REALVIEW_CLCD_EB,
+       },
+       {
+               .compatible = "arm,realview-pb1176-syscon",
+               .data = (void *)REALVIEW_CLCD_PB1176,
+       },
+       {
+               .compatible = "arm,realview-pb11mp-syscon",
+               .data = (void *)REALVIEW_CLCD_PB11MP,
+       },
+       {
+               .compatible = "arm,realview-pba8-syscon",
+               .data = (void *)REALVIEW_CLCD_PBA8,
+       },
+       {
+               .compatible = "arm,realview-pbx-syscon",
+               .data = (void *)REALVIEW_CLCD_PBX,
+       },
+       {},
+};
+
+/*
+ * Core module CLCD control on the Integrator/CP, bits
+ * 8 thru 19 of the CM_CONTROL register controls a bunch
+ * of CLCD settings.
+ */
+#define INTEGRATOR_HDR_CTRL_OFFSET     0x0C
+#define INTEGRATOR_CLCD_LCDBIASEN      BIT(8)
+#define INTEGRATOR_CLCD_LCDBIASUP      BIT(9)
+#define INTEGRATOR_CLCD_LCDBIASDN      BIT(10)
+/* Bits 11,12,13 controls the LCD type */
+#define INTEGRATOR_CLCD_LCDMUX_MASK    (BIT(11)|BIT(12)|BIT(13))
+#define INTEGRATOR_CLCD_LCDMUX_LCD24   BIT(11)
+#define INTEGRATOR_CLCD_LCDMUX_VGA565  BIT(12)
+#define INTEGRATOR_CLCD_LCDMUX_SHARP   (BIT(11)|BIT(12))
+#define INTEGRATOR_CLCD_LCDMUX_VGA555  BIT(13)
+#define INTEGRATOR_CLCD_LCDMUX_VGA24   (BIT(11)|BIT(12)|BIT(13))
+#define INTEGRATOR_CLCD_LCD0_EN                BIT(14)
+#define INTEGRATOR_CLCD_LCD1_EN                BIT(15)
+/* R/L flip on Sharp */
+#define INTEGRATOR_CLCD_LCD_STATIC1    BIT(16)
+/* U/D flip on Sharp */
+#define INTEGRATOR_CLCD_LCD_STATIC2    BIT(17)
+/* No connection on Sharp */
+#define INTEGRATOR_CLCD_LCD_STATIC     BIT(18)
+/* 0 = 24bit VGA, 1 = 18bit VGA */
+#define INTEGRATOR_CLCD_LCD_N24BITEN   BIT(19)
+
+#define INTEGRATOR_CLCD_MASK           (INTEGRATOR_CLCD_LCDBIASEN | \
+                                        INTEGRATOR_CLCD_LCDBIASUP | \
+                                        INTEGRATOR_CLCD_LCDBIASDN | \
+                                        INTEGRATOR_CLCD_LCDMUX_MASK | \
+                                        INTEGRATOR_CLCD_LCD0_EN | \
+                                        INTEGRATOR_CLCD_LCD1_EN | \
+                                        INTEGRATOR_CLCD_LCD_STATIC1 | \
+                                        INTEGRATOR_CLCD_LCD_STATIC2 | \
+                                        INTEGRATOR_CLCD_LCD_STATIC | \
+                                        INTEGRATOR_CLCD_LCD_N24BITEN)
+
+static void integrator_clcd_enable(struct clcd_fb *fb)
+{
+       struct fb_var_screeninfo *var = &fb->fb.var;
+       u32 val;
+
+       dev_info(&fb->dev->dev, "enable Integrator CLCD connectors\n");
+
+       /* FIXME: really needed? */
+       val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 |
+               INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN;
+       if (var->bits_per_pixel <= 8 ||
+           (var->bits_per_pixel == 16 && var->green.length == 5))
+               /* Pseudocolor, RGB555, BGR555 */
+               val |= INTEGRATOR_CLCD_LCDMUX_VGA555;
+       else if (fb->fb.var.bits_per_pixel <= 16)
+               /* truecolor RGB565 */
+               val |= INTEGRATOR_CLCD_LCDMUX_VGA565;
+       else
+               val = 0; /* no idea for this, don't trust the docs */
+
+       regmap_update_bits(versatile_syscon_map,
+                          INTEGRATOR_HDR_CTRL_OFFSET,
+                          INTEGRATOR_CLCD_MASK,
+                          val);
+}
+
+/*
+ * This configuration register in the Versatile and RealView
+ * family is uniformly present but appears more and more
+ * unutilized starting with the RealView series.
+ */
+#define SYS_CLCD                       0x50
+#define SYS_CLCD_MODE_MASK             (BIT(0)|BIT(1))
+#define SYS_CLCD_MODE_888              0
+#define SYS_CLCD_MODE_5551             BIT(0)
+#define SYS_CLCD_MODE_565_R_LSB                BIT(1)
+#define SYS_CLCD_MODE_565_B_LSB                (BIT(0)|BIT(1))
+#define SYS_CLCD_CONNECTOR_MASK                (BIT(2)|BIT(3)|BIT(4)|BIT(5))
+#define SYS_CLCD_NLCDIOON              BIT(2)
+#define SYS_CLCD_VDDPOSSWITCH          BIT(3)
+#define SYS_CLCD_PWR3V5SWITCH          BIT(4)
+#define SYS_CLCD_VDDNEGSWITCH          BIT(5)
+#define SYS_CLCD_TSNSS                 BIT(6) /* touchscreen enable */
+#define SYS_CLCD_SSPEXP                        BIT(7) /* SSP expansion enable */
+
+/* The Versatile can detect the connected panel type */
+#define SYS_CLCD_CLCDID_MASK           (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12))
+#define SYS_CLCD_ID_SANYO_3_8          (0x00 << 8)
+#define SYS_CLCD_ID_SHARP_8_4          (0x01 << 8)
+#define SYS_CLCD_ID_EPSON_2_2          (0x02 << 8)
+#define SYS_CLCD_ID_SANYO_2_5          (0x07 << 8)
+#define SYS_CLCD_ID_VGA                        (0x1f << 8)
+
+#define SYS_CLCD_TSNDAV                        BIT(13) /* data ready from TS */
+
+/* IB2 control register for the Versatile daughterboard */
+#define IB2_CTRL                       0x00
+#define IB2_CTRL_LCD_SD                        BIT(1) /* 1 = shut down LCD */
+#define IB2_CTRL_LCD_BL_ON             BIT(0)
+#define IB2_CTRL_LCD_MASK              (BIT(0)|BIT(1))
+
+static void versatile_clcd_disable(struct clcd_fb *fb)
+{
+       dev_info(&fb->dev->dev, "disable Versatile CLCD connectors\n");
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          0);
+
+       /* If we're on an IB2 daughterboard, turn off display */
+       if (versatile_ib2_map) {
+               dev_info(&fb->dev->dev, "disable IB2 display\n");
+               regmap_update_bits(versatile_ib2_map,
+                                  IB2_CTRL,
+                                  IB2_CTRL_LCD_MASK,
+                                  IB2_CTRL_LCD_SD);
+       }
+}
+
+static void versatile_clcd_enable(struct clcd_fb *fb)
+{
+       struct fb_var_screeninfo *var = &fb->fb.var;
+       u32 val = 0;
+
+       dev_info(&fb->dev->dev, "enable Versatile CLCD connectors\n");
+       switch (var->green.length) {
+       case 5:
+               val |= SYS_CLCD_MODE_5551;
+               break;
+       case 6:
+               if (var->red.offset == 0)
+                       val |= SYS_CLCD_MODE_565_R_LSB;
+               else
+                       val |= SYS_CLCD_MODE_565_B_LSB;
+               break;
+       case 8:
+               val |= SYS_CLCD_MODE_888;
+               break;
+       }
+
+       /* Set up the MUX */
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_MODE_MASK,
+                          val);
+
+       /* Then enable the display */
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
+
+       /* If we're on an IB2 daughterboard, turn on display */
+       if (versatile_ib2_map) {
+               dev_info(&fb->dev->dev, "enable IB2 display\n");
+               regmap_update_bits(versatile_ib2_map,
+                                  IB2_CTRL,
+                                  IB2_CTRL_LCD_MASK,
+                                  IB2_CTRL_LCD_BL_ON);
+       }
+}
+
+static void versatile_clcd_decode(struct clcd_fb *fb, struct clcd_regs *regs)
+{
+       clcdfb_decode(fb, regs);
+
+       /* Always clear BGR for RGB565: we do the routing externally */
+       if (fb->fb.var.green.length == 6)
+               regs->cntl &= ~CNTL_BGR;
+}
+
+static void realview_clcd_disable(struct clcd_fb *fb)
+{
+       dev_info(&fb->dev->dev, "disable RealView CLCD connectors\n");
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          0);
+}
+
+static void realview_clcd_enable(struct clcd_fb *fb)
+{
+       dev_info(&fb->dev->dev, "enable RealView CLCD connectors\n");
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
+}
+
+struct versatile_panel {
+       u32 id;
+       char *compatible;
+       bool ib2;
+};
+
+static const struct versatile_panel versatile_panels[] = {
+       {
+               .id = SYS_CLCD_ID_VGA,
+               .compatible = "VGA",
+       },
+       {
+               .id = SYS_CLCD_ID_SANYO_3_8,
+               .compatible = "sanyo,tm38qv67a02a",
+       },
+       {
+               .id = SYS_CLCD_ID_SHARP_8_4,
+               .compatible = "sharp,lq084v1dg21",
+       },
+       {
+               .id = SYS_CLCD_ID_EPSON_2_2,
+               .compatible = "epson,l2f50113t00",
+       },
+       {
+               .id = SYS_CLCD_ID_SANYO_2_5,
+               .compatible = "sanyo,alr252rgt",
+               .ib2 = true,
+       },
+};
+
+static void versatile_panel_probe(struct device *dev,
+                                 struct device_node *endpoint)
+{
+       struct versatile_panel const *vpanel = NULL;
+       struct device_node *panel = NULL;
+       u32 val;
+       int ret;
+       int i;
+
+       /*
+        * The Versatile CLCD has a panel auto-detection mechanism.
+        * We use this and look for the compatible panel in the
+        * device tree.
+        */
+       ret = regmap_read(versatile_syscon_map, SYS_CLCD, &val);
+       if (ret) {
+               dev_err(dev, "cannot read CLCD syscon register\n");
+               return;
+       }
+       val &= SYS_CLCD_CLCDID_MASK;
+
+       /* First find corresponding panel information */
+       for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) {
+               vpanel = &versatile_panels[i];
+
+               if (val == vpanel->id) {
+                       dev_err(dev, "autodetected panel \"%s\"\n",
+                               vpanel->compatible);
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(versatile_panels)) {
+               dev_err(dev, "could not auto-detect panel\n");
+               return;
+       }
+
+       panel = of_graph_get_remote_port_parent(endpoint);
+       if (!panel) {
+               dev_err(dev, "could not locate panel in DT\n");
+               return;
+       }
+       if (!of_device_is_compatible(panel, vpanel->compatible))
+               dev_err(dev, "panel in DT is not compatible with the "
+                       "auto-detected panel, continuing anyway\n");
+
+       /*
+        * If we have a Sanyo 2.5" port
+        * that we're running on an IB2 and proceed to look for the
+        * IB2 syscon regmap.
+        */
+       if (!vpanel->ib2)
+               return;
+
+       versatile_ib2_map = syscon_regmap_lookup_by_compatible(
+               "arm,versatile-ib2-syscon");
+       if (IS_ERR(versatile_ib2_map)) {
+               dev_err(dev, "could not locate IB2 control register\n");
+               versatile_ib2_map = NULL;
+               return;
+       }
+}
+
+int versatile_clcd_init_panel(struct clcd_fb *fb,
+                             struct device_node *endpoint)
+{
+       const struct of_device_id *clcd_id;
+       enum versatile_clcd versatile_clcd_type;
+       struct device_node *np;
+       struct regmap *map;
+       struct device *dev = &fb->dev->dev;
+
+       np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match,
+                                            &clcd_id);
+       if (!np) {
+               dev_err(dev, "no Versatile syscon node\n");
+               return -ENODEV;
+       }
+       versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
+
+       map = syscon_node_to_regmap(np);
+       if (IS_ERR(map)) {
+               dev_err(dev, "no Versatile syscon regmap\n");
+               return PTR_ERR(map);
+       }
+
+       switch (versatile_clcd_type) {
+       case INTEGRATOR_CLCD_CM:
+               versatile_syscon_map = map;
+               fb->board->enable = integrator_clcd_enable;
+               /* Override the caps, we have only these */
+               fb->board->caps = CLCD_CAP_5551 | CLCD_CAP_RGB565 |
+                       CLCD_CAP_888;
+               dev_info(dev, "set up callbacks for Integrator PL110\n");
+               break;
+       case VERSATILE_CLCD:
+               versatile_syscon_map = map;
+               fb->board->enable = versatile_clcd_enable;
+               fb->board->disable = versatile_clcd_disable;
+               fb->board->decode = versatile_clcd_decode;
+               versatile_panel_probe(dev, endpoint);
+               dev_info(dev, "set up callbacks for Versatile\n");
+               break;
+       case REALVIEW_CLCD_EB:
+       case REALVIEW_CLCD_PB1176:
+       case REALVIEW_CLCD_PB11MP:
+       case REALVIEW_CLCD_PBA8:
+       case REALVIEW_CLCD_PBX:
+               versatile_syscon_map = map;
+               fb->board->enable = realview_clcd_enable;
+               fb->board->disable = realview_clcd_disable;
+               dev_info(dev, "set up callbacks for RealView PL111\n");
+               break;
+       default:
+               dev_info(dev, "unknown Versatile system controller\n");
+               break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(versatile_clcd_init_panel);
+#endif
diff --git a/drivers/video/fbdev/amba-clcd-versatile.h b/drivers/video/fbdev/amba-clcd-versatile.h
new file mode 100644 (file)
index 0000000..1b14359
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Special local versatile callbacks
+ */
+#include <linux/of.h>
+#include <linux/amba/bus.h>
+#include <linux/platform_data/video-clcd-versatile.h>
+
+#if defined(CONFIG_PLAT_VERSATILE_CLCD) && defined(CONFIG_OF)
+int versatile_clcd_init_panel(struct clcd_fb *fb,
+                             struct device_node *endpoint);
+#else
+static inline int versatile_clcd_init_panel(struct clcd_fb *fb,
+                               struct device_node *endpoint)
+{
+       return 0;
+}
+#endif
index 9b158869cb89158acc0178f034708a276f2f839c..ec2671d98abcccb5f3afa6bfd68158a14557ce6e 100644 (file)
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_graph.h>
+#include <linux/backlight.h>
 #include <video/display_timing.h>
 #include <video/of_display_timing.h>
 #include <video/videomode.h>
 
+#include "amba-clcd-nomadik.h"
+#include "amba-clcd-versatile.h"
+
 #define to_clcd(info)  container_of(info, struct clcd_fb, fb)
 
 /* This is limited to 16 characters when displayed by X startup */
@@ -71,6 +75,11 @@ static void clcdfb_disable(struct clcd_fb *fb)
        if (fb->board->disable)
                fb->board->disable(fb);
 
+       if (fb->panel->backlight) {
+               fb->panel->backlight->props.power = FB_BLANK_POWERDOWN;
+               backlight_update_status(fb->panel->backlight);
+       }
+
        val = readl(fb->regs + fb->off_cntl);
        if (val & CNTL_LCDPWR) {
                val &= ~CNTL_LCDPWR;
@@ -116,6 +125,14 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl)
        cntl |= CNTL_LCDPWR;
        writel(cntl, fb->regs + fb->off_cntl);
 
+       /*
+        * Turn on backlight
+        */
+       if (fb->panel->backlight) {
+               fb->panel->backlight->props.power = FB_BLANK_UNBLANK;
+               backlight_update_status(fb->panel->backlight);
+       }
+
        /*
         * finally, enable the interface.
         */
@@ -211,6 +228,15 @@ clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var)
                        var->blue.length = 4;
                }
                break;
+       case 24:
+               if (fb->vendor->packed_24_bit_pixels) {
+                       var->red.length = 8;
+                       var->green.length = 8;
+                       var->blue.length = 8;
+               } else {
+                       ret = -EINVAL;
+               }
+               break;
        case 32:
                /* If we can't do 888, reject */
                caps &= CLCD_CAP_888;
@@ -297,6 +323,12 @@ static int clcdfb_set_par(struct fb_info *info)
 
        clcdfb_disable(fb);
 
+       /* Some variants must be clocked here */
+       if (fb->vendor->clock_timregs && !fb->clk_enabled) {
+               fb->clk_enabled = true;
+               clk_enable(fb->clk);
+       }
+
        writel(regs.tim0, fb->regs + CLCD_TIM0);
        writel(regs.tim1, fb->regs + CLCD_TIM1);
        writel(regs.tim2, fb->regs + CLCD_TIM2);
@@ -551,7 +583,7 @@ static int clcdfb_register(struct clcd_fb *fb)
 
 #ifdef CONFIG_OF
 static int clcdfb_of_get_dpi_panel_mode(struct device_node *node,
-               struct fb_videomode *mode)
+               struct clcd_panel *clcd_panel)
 {
        int err;
        struct display_timing timing;
@@ -563,10 +595,31 @@ static int clcdfb_of_get_dpi_panel_mode(struct device_node *node,
 
        videomode_from_timing(&timing, &video);
 
-       err = fb_videomode_from_videomode(&video, mode);
+       err = fb_videomode_from_videomode(&video, &clcd_panel->mode);
        if (err)
                return err;
 
+       /* Set up some inversion flags */
+       if (timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+               clcd_panel->tim2 |= TIM2_IPC;
+       else if (!(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE))
+               /*
+                * To preserve backwards compatibility, the IPC (inverted
+                * pixel clock) flag needs to be set on any display that
+                * doesn't explicitly specify that the pixel clock is
+                * active on the negative or positive edge.
+                */
+               clcd_panel->tim2 |= TIM2_IPC;
+
+       if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW)
+               clcd_panel->tim2 |= TIM2_IHS;
+
+       if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW)
+               clcd_panel->tim2 |= TIM2_IVS;
+
+       if (timing.flags & DISPLAY_FLAGS_DE_LOW)
+               clcd_panel->tim2 |= TIM2_IOE;
+
        return 0;
 }
 
@@ -576,11 +629,34 @@ static int clcdfb_snprintf_mode(char *buf, int size, struct fb_videomode *mode)
                        mode->refresh);
 }
 
+static int clcdfb_of_get_backlight(struct device_node *endpoint,
+                                  struct clcd_panel *clcd_panel)
+{
+       struct device_node *panel;
+       struct device_node *backlight;
+
+       panel = of_graph_get_remote_port_parent(endpoint);
+       if (!panel)
+               return -ENODEV;
+
+       /* Look up the optional backlight phandle */
+       backlight = of_parse_phandle(panel, "backlight", 0);
+       if (backlight) {
+               clcd_panel->backlight = of_find_backlight_by_node(backlight);
+               of_node_put(backlight);
+
+               if (!clcd_panel->backlight)
+                       return -EPROBE_DEFER;
+       }
+       return 0;
+}
+
 static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint,
-               struct fb_videomode *mode)
+               struct clcd_panel *clcd_panel)
 {
        int err;
        struct device_node *panel;
+       struct fb_videomode *mode;
        char *name;
        int len;
 
@@ -590,11 +666,12 @@ static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint,
 
        /* Only directly connected DPI panels supported for now */
        if (of_device_is_compatible(panel, "panel-dpi"))
-               err = clcdfb_of_get_dpi_panel_mode(panel, mode);
+               err = clcdfb_of_get_dpi_panel_mode(panel, clcd_panel);
        else
                err = -ENOENT;
        if (err)
                return err;
+       mode = &clcd_panel->mode;
 
        len = clcdfb_snprintf_mode(NULL, 0, mode);
        name = devm_kzalloc(dev, len + 1, GFP_KERNEL);
@@ -616,6 +693,7 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
        } panels[] = {
                { 0x110, 1,  7, 13, CLCD_CAP_5551 },
                { 0x110, 0,  8, 16, CLCD_CAP_888 },
+               { 0x110, 16, 8, 0,  CLCD_CAP_888 },
                { 0x111, 4, 14, 20, CLCD_CAP_444 },
                { 0x111, 3, 11, 19, CLCD_CAP_444 | CLCD_CAP_5551 },
                { 0x111, 3, 10, 19, CLCD_CAP_444 | CLCD_CAP_5551 |
@@ -625,8 +703,8 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
        };
        int i;
 
-       /* Bypass pixel clock divider, data output on the falling edge */
-       fb->panel->tim2 = TIM2_BCD | TIM2_IPC;
+       /* Bypass pixel clock divider */
+       fb->panel->tim2 |= TIM2_BCD;
 
        /* TFT display, vert. comp. interrupt at the start of the back porch */
        fb->panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);
@@ -643,6 +721,49 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
                        fb->panel->caps = panels[i].caps;
        }
 
+       /*
+        * If we actually physically connected the R lines to B and
+        * vice versa
+        */
+       if (r0 != 0 && b0 == 0)
+               fb->panel->bgr_connection = true;
+
+       if (fb->panel->caps && fb->vendor->st_bitmux_control) {
+               /*
+                * Set up the special bits for the Nomadik control register
+                * (other platforms tend to do this through an external
+                * register).
+                */
+
+               /* Offset of the highest used color */
+               int maxoff = max3(r0, g0, b0);
+               /* Most significant bit out, highest used bit */
+               int msb = 0;
+
+               if (fb->panel->caps & CLCD_CAP_888) {
+                       msb = maxoff + 8 - 1;
+               } else if (fb->panel->caps & CLCD_CAP_565) {
+                       msb = maxoff + 5 - 1;
+                       fb->panel->cntl |= CNTL_ST_1XBPP_565;
+               } else if (fb->panel->caps & CLCD_CAP_5551) {
+                       msb = maxoff + 5 - 1;
+                       fb->panel->cntl |= CNTL_ST_1XBPP_5551;
+               } else if (fb->panel->caps & CLCD_CAP_444) {
+                       msb = maxoff + 4 - 1;
+                       fb->panel->cntl |= CNTL_ST_1XBPP_444;
+               }
+
+               /* Send out as many bits as we need */
+               if (msb > 17)
+                       fb->panel->cntl |= CNTL_ST_CDWID_24;
+               else if (msb > 15)
+                       fb->panel->cntl |= CNTL_ST_CDWID_18;
+               else if (msb > 11)
+                       fb->panel->cntl |= CNTL_ST_CDWID_16;
+               else
+                       fb->panel->cntl |= CNTL_ST_CDWID_12;
+       }
+
        return fb->panel->caps ? 0 : -EINVAL;
 }
 
@@ -658,11 +779,24 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
        if (!fb->panel)
                return -ENOMEM;
 
+       /*
+        * Fetch the panel endpoint.
+        */
        endpoint = of_graph_get_next_endpoint(fb->dev->dev.of_node, NULL);
        if (!endpoint)
                return -ENODEV;
 
-       err = clcdfb_of_get_mode(&fb->dev->dev, endpoint, &fb->panel->mode);
+       if (fb->vendor->init_panel) {
+               err = fb->vendor->init_panel(fb, endpoint);
+               if (err)
+                       return err;
+       }
+
+       err = clcdfb_of_get_backlight(endpoint, fb->panel);
+       if (err)
+               return err;
+
+       err = clcdfb_of_get_mode(&fb->dev->dev, endpoint, fb->panel);
        if (err)
                return err;
 
@@ -693,11 +827,11 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
 
        if (of_property_read_u32_array(endpoint,
                        "arm,pl11x,tft-r0g0b0-pads",
-                       tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) == 0)
-               return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0],
-                                tft_r0b0g0[1],  tft_r0b0g0[2]);
+                       tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) != 0)
+               return -ENOENT;
 
-       return -ENOENT;
+       return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0],
+                                       tft_r0b0g0[1],  tft_r0b0g0[2]);
 }
 
 static int clcdfb_of_vram_setup(struct clcd_fb *fb)
@@ -818,6 +952,7 @@ static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev)
 static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
 {
        struct clcd_board *board = dev_get_platdata(&dev->dev);
+       struct clcd_vendor_data *vendor = id->data;
        struct clcd_fb *fb;
        int ret;
 
@@ -827,6 +962,12 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
        if (!board)
                return -EINVAL;
 
+       if (vendor->init_board) {
+               ret = vendor->init_board(dev, board);
+               if (ret)
+                       return ret;
+       }
+
        ret = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
        if (ret)
                goto out;
@@ -845,17 +986,18 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
        }
 
        fb->dev = dev;
+       fb->vendor = vendor;
        fb->board = board;
 
-       dev_info(&fb->dev->dev, "PL%03x rev%u at 0x%08llx\n",
-               amba_part(dev), amba_rev(dev),
+       dev_info(&fb->dev->dev, "PL%03x designer %02x rev%u at 0x%08llx\n",
+               amba_part(dev), amba_manf(dev), amba_rev(dev),
                (unsigned long long)dev->res.start);
 
        ret = fb->board->setup(fb);
        if (ret)
                goto free_fb;
 
-       ret = clcdfb_register(fb); 
+       ret = clcdfb_register(fb);
        if (ret == 0) {
                amba_set_drvdata(dev, fb);
                goto out;
@@ -891,10 +1033,30 @@ static int clcdfb_remove(struct amba_device *dev)
        return 0;
 }
 
+static struct clcd_vendor_data vendor_arm = {
+       /* Sets up the versatile board displays */
+       .init_panel = versatile_clcd_init_panel,
+};
+
+static struct clcd_vendor_data vendor_nomadik = {
+       .clock_timregs = true,
+       .packed_24_bit_pixels = true,
+       .st_bitmux_control = true,
+       .init_board = nomadik_clcd_init_board,
+       .init_panel = nomadik_clcd_init_panel,
+};
+
 static struct amba_id clcdfb_id_table[] = {
        {
                .id     = 0x00041110,
                .mask   = 0x000ffffe,
+               .data   = &vendor_arm,
+       },
+       /* ST Electronics Nomadik variant */
+       {
+               .id     = 0x00180110,
+               .mask   = 0x00fffffe,
+               .data   = &vendor_nomadik,
        },
        { 0, 0 },
 };
index 1b0b233b8b39858f10bf54459884d084e858bb36..1928cb2b5386fad37b55f1e6995cfaef88b979e2 100644 (file)
@@ -79,7 +79,7 @@ struct arcfb_par {
        spinlock_t lock;
 };
 
-static struct fb_fix_screeninfo arcfb_fix = {
+static const struct fb_fix_screeninfo arcfb_fix = {
        .id =           "arcfb",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_MONO01,
@@ -89,7 +89,7 @@ static struct fb_fix_screeninfo arcfb_fix = {
        .accel =        FB_ACCEL_NONE,
 };
 
-static struct fb_var_screeninfo arcfb_var = {
+static const struct fb_var_screeninfo arcfb_var = {
        .xres           = 128,
        .yres           = 64,
        .xres_virtual   = 128,
index 7e8ddf00ccc2d502673022542e3dac453ab83805..91eea4583382dfa12a20204eb907789d28e4d4ba 100644 (file)
@@ -474,7 +474,7 @@ static void chips_hw_init(struct fb_info *p)
                write_fr(chips_init_fr[i].addr, chips_init_fr[i].data);
 }
 
-static struct fb_fix_screeninfo asiliantfb_fix = {
+static const struct fb_fix_screeninfo asiliantfb_fix = {
        .id =           "Asiliant 69000",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_PSEUDOCOLOR,
@@ -483,7 +483,7 @@ static struct fb_fix_screeninfo asiliantfb_fix = {
        .smem_len =     0x200000,       /* 2MB */
 };
 
-static struct fb_var_screeninfo asiliantfb_var = {
+static const struct fb_var_screeninfo asiliantfb_var = {
        .xres           = 640,
        .yres           = 480,
        .xres_virtual   = 640,
index 0a4626886b00c1402bc2bbc234fe691bb549ff8a..fa07242a78d2910966db4492618b7e5d5ca3282f 100644 (file)
@@ -93,7 +93,7 @@
 
 #ifndef CONFIG_PPC_PMAC
 /* default mode */
-static struct fb_var_screeninfo default_var = {
+static const struct fb_var_screeninfo default_var = {
        /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
        640, 480, 640, 480, 0, 0, 8, 0,
        {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
@@ -104,7 +104,7 @@ static struct fb_var_screeninfo default_var = {
 #else /* CONFIG_PPC_PMAC */
 /* default to 1024x768 at 75Hz on PPC - this will work
  * on the iMac, the usual 640x480 @ 60Hz doesn't. */
-static struct fb_var_screeninfo default_var = {
+static const struct fb_var_screeninfo default_var = {
        /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */
        1024, 768, 1024, 768, 0, 0, 8, 0,
        {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
@@ -375,7 +375,7 @@ static const struct aty128_meminfo ddr_sgram = {
        .name = "64-bit DDR SGRAM",
 };
 
-static struct fb_fix_screeninfo aty128fb_fix = {
+static const struct fb_fix_screeninfo aty128fb_fix = {
        .id             = "ATY Rage128",
        .type           = FB_TYPE_PACKED_PIXELS,
        .visual         = FB_VISUAL_PSEUDOCOLOR,
index f34ed47fcaf82e71b1fa25386226389a9bfbe670..11026e726b68468d2e8de7e159ad5003001b5610 100644 (file)
@@ -212,7 +212,7 @@ struct pci_mmap_map {
        unsigned long prot_mask;
 };
 
-static struct fb_fix_screeninfo atyfb_fix = {
+static const struct fb_fix_screeninfo atyfb_fix = {
        .id             = "ATY Mach64",
        .type           = FB_TYPE_PACKED_PIXELS,
        .visual         = FB_VISUAL_PSEUDOCOLOR,
index f1ce229de78d7c3ad8f351bfcc8d7a5583f94d66..278b421ab3fedc607b53330648938782d9fb3e88 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "../edid.h"
 
-static struct fb_var_screeninfo radeonfb_default_var = {
+static const struct fb_var_screeninfo radeonfb_default_var = {
        .xres           = 640,
        .yres           = 480,
        .xres_virtual   = 640,
index e2d7d039ce3b23e8f8fec2fbe863a1c8e04ca46f..542ffaddc6ab43fd75a1a38cdd8350e7d6692a07 100644 (file)
@@ -375,7 +375,6 @@ static int bfin_adv7393_fb_probe(struct i2c_client *client,
 {
        int ret = 0;
        struct proc_dir_entry *entry;
-       int num_modes = ARRAY_SIZE(known_modes);
 
        struct adv7393fb_device *fbdev = NULL;
 
@@ -384,7 +383,7 @@ static int bfin_adv7393_fb_probe(struct i2c_client *client,
                return -EINVAL;
        }
 
-       if (mode > num_modes) {
+       if (mode >= ARRAY_SIZE(known_modes)) {
                dev_err(&client->dev, "mode %d: not supported", mode);
                return -EFAULT;
        }
@@ -797,7 +796,7 @@ static struct i2c_driver bfin_adv7393_fb_driver = {
 
 static int __init bfin_adv7393_fb_driver_init(void)
 {
-#if  defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE)
+#if IS_ENABLED(CONFIG_I2C_BLACKFIN_TWI)
        request_module("i2c-bfin-twi");
 #else
        request_module("i2c-gpio");
index 07675d6f323e774b4549ab7d4bb1e8a90dff0663..2d3b691f3fc4885414ae9234950d8e9c798e5fe6 100644 (file)
@@ -63,7 +63,6 @@
 #define LCD_CUR_POS(x)         ((x) & LCD_CUR_POS_MASK)
 #define LCD_TEXT_POS(x)                ((x) | LCD_TEXT_MODE)
 
-#ifdef CONFIG_MIPS_COBALT
 static inline void lcd_write_control(struct fb_info *info, u8 control)
 {
        writel((u32)control << 24, info->screen_base);
@@ -83,47 +82,6 @@ static inline u8 lcd_read_data(struct fb_info *info)
 {
        return readl(info->screen_base + LCD_DATA_REG_OFFSET) >> 24;
 }
-#else
-
-#define LCD_CTL                        0x00
-#define LCD_DATA               0x08
-#define CPLD_STATUS            0x10
-#define CPLD_DATA              0x18
-
-static inline void cpld_wait(struct fb_info *info)
-{
-       do {
-       } while (readl(info->screen_base + CPLD_STATUS) & 1);
-}
-
-static inline void lcd_write_control(struct fb_info *info, u8 control)
-{
-       cpld_wait(info);
-       writel(control, info->screen_base + LCD_CTL);
-}
-
-static inline u8 lcd_read_control(struct fb_info *info)
-{
-       cpld_wait(info);
-       readl(info->screen_base + LCD_CTL);
-       cpld_wait(info);
-       return readl(info->screen_base + CPLD_DATA) & 0xff;
-}
-
-static inline void lcd_write_data(struct fb_info *info, u8 data)
-{
-       cpld_wait(info);
-       writel(data, info->screen_base + LCD_DATA);
-}
-
-static inline u8 lcd_read_data(struct fb_info *info)
-{
-       cpld_wait(info);
-       readl(info->screen_base + LCD_DATA);
-       cpld_wait(info);
-       return readl(info->screen_base + CPLD_DATA) & 0xff;
-}
-#endif
 
 static int lcd_busy_wait(struct fb_info *info)
 {
index 924bad45c17642c778932a9a00b4bb8f19d4f268..37a37c4d04cbeb7211c5a02cef724cf47b16de5f 100644 (file)
@@ -50,9 +50,9 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
                return 1;
 
        if (regno < 16) {
-               red   >>= 8;
-               green >>= 8;
-               blue  >>= 8;
+               red   >>= 16 - info->var.red.length;
+               green >>= 16 - info->var.green.length;
+               blue  >>= 16 - info->var.blue.length;
                ((u32 *)(info->pseudo_palette))[regno] =
                        (red   << info->var.red.offset)   |
                        (green << info->var.green.offset) |
diff --git a/drivers/video/fbdev/exynos/Kconfig b/drivers/video/fbdev/exynos/Kconfig
deleted file mode 100644 (file)
index d916bef..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#
-# Exynos Video configuration
-#
-
-menuconfig EXYNOS_VIDEO
-       tristate "Exynos Video driver support"
-       depends on ARCH_S5PV210 || ARCH_EXYNOS
-       help
-         This enables support for EXYNOS Video device.
-
-if EXYNOS_VIDEO
-
-#
-# MIPI DSI driver
-#
-
-config EXYNOS_MIPI_DSI
-       tristate "EXYNOS MIPI DSI driver support."
-       select GENERIC_PHY
-       help
-         This enables support for MIPI-DSI device.
-
-config EXYNOS_LCD_S6E8AX0
-       tristate "S6E8AX0 MIPI AMOLED LCD Driver"
-       depends on EXYNOS_MIPI_DSI && BACKLIGHT_CLASS_DEVICE
-       depends on (LCD_CLASS_DEVICE = y)
-       default n
-       help
-         If you have an S6E8AX0 MIPI AMOLED LCD Panel, say Y to enable its
-         LCD control driver.
-
-endif # EXYNOS_VIDEO
diff --git a/drivers/video/fbdev/exynos/Makefile b/drivers/video/fbdev/exynos/Makefile
deleted file mode 100644 (file)
index 02d8dc5..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Makefile for the exynos video drivers.
-#
-
-obj-$(CONFIG_EXYNOS_MIPI_DSI)          += exynos-mipi-dsi-mod.o
-
-exynos-mipi-dsi-mod-objs               += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
-                                          exynos_mipi_dsi_lowlevel.o
-obj-$(CONFIG_EXYNOS_LCD_S6E8AX0)       += s6e8ax0.o
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi.c
deleted file mode 100644 (file)
index 92e4af3..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-/* linux/drivers/video/exynos/exynos_mipi_dsi.c
- *
- * Samsung SoC MIPI-DSIM driver.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae, <inki.dae@samsung.com>
- * Donghwa Lee, <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/clk.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/fb.h>
-#include <linux/ctype.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/memory.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/kthread.h>
-#include <linux/notifier.h>
-#include <linux/phy/phy.h>
-#include <linux/regulator/consumer.h>
-#include <linux/pm_runtime.h>
-#include <linux/err.h>
-
-#include <video/exynos_mipi_dsim.h>
-
-#include "exynos_mipi_dsi_common.h"
-#include "exynos_mipi_dsi_lowlevel.h"
-
-struct mipi_dsim_ddi {
-       int                             bus_id;
-       struct list_head                list;
-       struct mipi_dsim_lcd_device     *dsim_lcd_dev;
-       struct mipi_dsim_lcd_driver     *dsim_lcd_drv;
-};
-
-static LIST_HEAD(dsim_ddi_list);
-
-static DEFINE_MUTEX(mipi_dsim_lock);
-
-static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device
-                                                       *pdev)
-{
-       return pdev->dev.platform_data;
-}
-
-static struct regulator_bulk_data supplies[] = {
-       { .supply = "vdd11", },
-       { .supply = "vdd18", },
-};
-
-static int exynos_mipi_regulator_enable(struct mipi_dsim_device *dsim)
-{
-       int ret;
-
-       mutex_lock(&dsim->lock);
-       ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
-       mutex_unlock(&dsim->lock);
-
-       return ret;
-}
-
-static int exynos_mipi_regulator_disable(struct mipi_dsim_device *dsim)
-{
-       int ret;
-
-       mutex_lock(&dsim->lock);
-       ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
-       mutex_unlock(&dsim->lock);
-
-       return ret;
-}
-
-/* update all register settings to MIPI DSI controller. */
-static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim)
-{
-       /*
-        * data from Display controller(FIMD) is not transferred in video mode
-        * but in case of command mode, all settings is not updated to
-        * registers.
-        */
-       exynos_mipi_dsi_stand_by(dsim, 0);
-
-       exynos_mipi_dsi_init_dsim(dsim);
-       exynos_mipi_dsi_init_link(dsim);
-
-       exynos_mipi_dsi_set_hs_enable(dsim);
-
-       /* set display timing. */
-       exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config);
-
-       exynos_mipi_dsi_init_interrupt(dsim);
-
-       /*
-        * data from Display controller(FIMD) is transferred in video mode
-        * but in case of command mode, all settings are updated to registers.
-        */
-       exynos_mipi_dsi_stand_by(dsim, 1);
-}
-
-static int exynos_mipi_dsi_early_blank_mode(struct mipi_dsim_device *dsim,
-               int power)
-{
-       struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
-       struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
-
-       switch (power) {
-       case FB_BLANK_POWERDOWN:
-               if (dsim->suspended)
-                       return 0;
-
-               if (client_drv && client_drv->suspend)
-                       client_drv->suspend(client_dev);
-
-               clk_disable(dsim->clock);
-
-               exynos_mipi_regulator_disable(dsim);
-
-               dsim->suspended = true;
-
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
-{
-       struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
-       struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
-
-       switch (power) {
-       case FB_BLANK_UNBLANK:
-               if (!dsim->suspended)
-                       return 0;
-
-               /* lcd panel power on. */
-               if (client_drv && client_drv->power_on)
-                       client_drv->power_on(client_dev, 1);
-
-               exynos_mipi_regulator_enable(dsim);
-
-               /* enable MIPI-DSI PHY. */
-               phy_power_on(dsim->phy);
-
-               clk_enable(dsim->clock);
-
-               exynos_mipi_update_cfg(dsim);
-
-               /* set lcd panel sequence commands. */
-               if (client_drv && client_drv->set_sequence)
-                       client_drv->set_sequence(client_dev);
-
-               dsim->suspended = false;
-
-               break;
-       case FB_BLANK_NORMAL:
-               /* TODO. */
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev)
-{
-       struct mipi_dsim_ddi *dsim_ddi;
-
-       if (!lcd_dev->name) {
-               pr_err("dsim_lcd_device name is NULL.\n");
-               return -EFAULT;
-       }
-
-       dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL);
-       if (!dsim_ddi) {
-               pr_err("failed to allocate dsim_ddi object.\n");
-               return -ENOMEM;
-       }
-
-       dsim_ddi->dsim_lcd_dev = lcd_dev;
-
-       mutex_lock(&mipi_dsim_lock);
-       list_add_tail(&dsim_ddi->list, &dsim_ddi_list);
-       mutex_unlock(&mipi_dsim_lock);
-
-       return 0;
-}
-
-static struct mipi_dsim_ddi *exynos_mipi_dsi_find_lcd_device(
-                                       struct mipi_dsim_lcd_driver *lcd_drv)
-{
-       struct mipi_dsim_ddi *dsim_ddi, *next;
-       struct mipi_dsim_lcd_device *lcd_dev;
-
-       mutex_lock(&mipi_dsim_lock);
-
-       list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
-               if (!dsim_ddi)
-                       goto out;
-
-               lcd_dev = dsim_ddi->dsim_lcd_dev;
-               if (!lcd_dev)
-                       continue;
-
-               if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) {
-                       /**
-                        * bus_id would be used to identify
-                        * connected bus.
-                        */
-                       dsim_ddi->bus_id = lcd_dev->bus_id;
-                       mutex_unlock(&mipi_dsim_lock);
-
-                       return dsim_ddi;
-               }
-
-               list_del(&dsim_ddi->list);
-               kfree(dsim_ddi);
-       }
-
-out:
-       mutex_unlock(&mipi_dsim_lock);
-
-       return NULL;
-}
-
-int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv)
-{
-       struct mipi_dsim_ddi *dsim_ddi;
-
-       if (!lcd_drv->name) {
-               pr_err("dsim_lcd_driver name is NULL.\n");
-               return -EFAULT;
-       }
-
-       dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv);
-       if (!dsim_ddi) {
-               pr_err("mipi_dsim_ddi object not found.\n");
-               return -EFAULT;
-       }
-
-       dsim_ddi->dsim_lcd_drv = lcd_drv;
-
-       pr_info("registered panel driver(%s) to mipi-dsi driver.\n",
-               lcd_drv->name);
-
-       return 0;
-
-}
-EXPORT_SYMBOL_GPL(exynos_mipi_dsi_register_lcd_driver);
-
-static struct mipi_dsim_ddi *exynos_mipi_dsi_bind_lcd_ddi(
-                                               struct mipi_dsim_device *dsim,
-                                               const char *name)
-{
-       struct mipi_dsim_ddi *dsim_ddi, *next;
-       struct mipi_dsim_lcd_driver *lcd_drv;
-       struct mipi_dsim_lcd_device *lcd_dev;
-       int ret;
-
-       mutex_lock(&dsim->lock);
-
-       list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
-               lcd_drv = dsim_ddi->dsim_lcd_drv;
-               lcd_dev = dsim_ddi->dsim_lcd_dev;
-               if (!lcd_drv || !lcd_dev ||
-                       (dsim->id != dsim_ddi->bus_id))
-                               continue;
-
-               dev_dbg(dsim->dev, "lcd_drv->id = %d, lcd_dev->id = %d\n",
-                               lcd_drv->id, lcd_dev->id);
-               dev_dbg(dsim->dev, "lcd_dev->bus_id = %d, dsim->id = %d\n",
-                               lcd_dev->bus_id, dsim->id);
-
-               if ((strcmp(lcd_drv->name, name) == 0)) {
-                       lcd_dev->master = dsim;
-
-                       lcd_dev->dev.parent = dsim->dev;
-                       dev_set_name(&lcd_dev->dev, "%s", lcd_drv->name);
-
-                       ret = device_register(&lcd_dev->dev);
-                       if (ret < 0) {
-                               dev_err(dsim->dev,
-                                       "can't register %s, status %d\n",
-                                       dev_name(&lcd_dev->dev), ret);
-                               mutex_unlock(&dsim->lock);
-
-                               return NULL;
-                       }
-
-                       dsim->dsim_lcd_dev = lcd_dev;
-                       dsim->dsim_lcd_drv = lcd_drv;
-
-                       mutex_unlock(&dsim->lock);
-
-                       return dsim_ddi;
-               }
-       }
-
-       mutex_unlock(&dsim->lock);
-
-       return NULL;
-}
-
-/* define MIPI-DSI Master operations. */
-static struct mipi_dsim_master_ops master_ops = {
-       .cmd_read                       = exynos_mipi_dsi_rd_data,
-       .cmd_write                      = exynos_mipi_dsi_wr_data,
-       .get_dsim_frame_done            = exynos_mipi_dsi_get_frame_done_status,
-       .clear_dsim_frame_done          = exynos_mipi_dsi_clear_frame_done,
-       .set_early_blank_mode           = exynos_mipi_dsi_early_blank_mode,
-       .set_blank_mode                 = exynos_mipi_dsi_blank_mode,
-};
-
-static int exynos_mipi_dsi_probe(struct platform_device *pdev)
-{
-       struct resource *res;
-       struct mipi_dsim_device *dsim;
-       struct mipi_dsim_config *dsim_config;
-       struct mipi_dsim_platform_data *dsim_pd;
-       struct mipi_dsim_ddi *dsim_ddi;
-       int ret = -EINVAL;
-
-       dsim = devm_kzalloc(&pdev->dev, sizeof(struct mipi_dsim_device),
-                               GFP_KERNEL);
-       if (!dsim) {
-               dev_err(&pdev->dev, "failed to allocate dsim object.\n");
-               return -ENOMEM;
-       }
-
-       dsim->pd = to_dsim_plat(pdev);
-       dsim->dev = &pdev->dev;
-       dsim->id = pdev->id;
-
-       /* get mipi_dsim_platform_data. */
-       dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd;
-       if (dsim_pd == NULL) {
-               dev_err(&pdev->dev, "failed to get platform data for dsim.\n");
-               return -EINVAL;
-       }
-       /* get mipi_dsim_config. */
-       dsim_config = dsim_pd->dsim_config;
-       if (dsim_config == NULL) {
-               dev_err(&pdev->dev, "failed to get dsim config data.\n");
-               return -EINVAL;
-       }
-
-       dsim->dsim_config = dsim_config;
-       dsim->master_ops = &master_ops;
-
-       mutex_init(&dsim->lock);
-
-       ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies),
-                                       supplies);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret);
-               return ret;
-       }
-
-       dsim->phy = devm_phy_get(&pdev->dev, "dsim");
-       if (IS_ERR(dsim->phy))
-               return PTR_ERR(dsim->phy);
-
-       dsim->clock = devm_clk_get(&pdev->dev, "dsim0");
-       if (IS_ERR(dsim->clock)) {
-               dev_err(&pdev->dev, "failed to get dsim clock source\n");
-               return -ENODEV;
-       }
-
-       clk_enable(dsim->clock);
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
-       dsim->reg_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(dsim->reg_base)) {
-               ret = PTR_ERR(dsim->reg_base);
-               goto error;
-       }
-
-       mutex_init(&dsim->lock);
-
-       /* bind lcd ddi matched with panel name. */
-       dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name);
-       if (!dsim_ddi) {
-               dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n");
-               ret = -EINVAL;
-               goto error;
-       }
-
-       ret = platform_get_irq(pdev, 0);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to request dsim irq resource\n");
-               goto error;
-       }
-       dsim->irq = ret;
-
-       init_completion(&dsim_wr_comp);
-       init_completion(&dsim_rd_comp);
-       platform_set_drvdata(pdev, dsim);
-
-       ret = devm_request_irq(&pdev->dev, dsim->irq,
-                       exynos_mipi_dsi_interrupt_handler,
-                       IRQF_SHARED, dev_name(&pdev->dev), dsim);
-       if (ret != 0) {
-               dev_err(&pdev->dev, "failed to request dsim irq\n");
-               ret = -EINVAL;
-               goto error;
-       }
-
-       /* enable interrupts */
-       exynos_mipi_dsi_init_interrupt(dsim);
-
-       /* initialize mipi-dsi client(lcd panel). */
-       if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe)
-               dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev);
-
-       /* in case mipi-dsi has been enabled by bootloader */
-       if (dsim_pd->enabled) {
-               exynos_mipi_regulator_enable(dsim);
-               goto done;
-       }
-
-       /* lcd panel power on. */
-       if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on)
-               dsim_ddi->dsim_lcd_drv->power_on(dsim_ddi->dsim_lcd_dev, 1);
-
-       exynos_mipi_regulator_enable(dsim);
-
-       /* enable MIPI-DSI PHY. */
-       phy_power_on(dsim->phy);
-
-       exynos_mipi_update_cfg(dsim);
-
-       /* set lcd panel sequence commands. */
-       if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->set_sequence)
-               dsim_ddi->dsim_lcd_drv->set_sequence(dsim_ddi->dsim_lcd_dev);
-
-       dsim->suspended = false;
-
-done:
-       platform_set_drvdata(pdev, dsim);
-
-       dev_dbg(&pdev->dev, "%s() completed successfully (%s mode)\n", __func__,
-               dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB");
-
-       return 0;
-
-error:
-       clk_disable(dsim->clock);
-       return ret;
-}
-
-static int exynos_mipi_dsi_remove(struct platform_device *pdev)
-{
-       struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
-       struct mipi_dsim_ddi *dsim_ddi, *next;
-       struct mipi_dsim_lcd_driver *dsim_lcd_drv;
-
-       clk_disable(dsim->clock);
-
-       list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
-               if (dsim_ddi) {
-                       if (dsim->id != dsim_ddi->bus_id)
-                               continue;
-
-                       dsim_lcd_drv = dsim_ddi->dsim_lcd_drv;
-
-                       if (dsim_lcd_drv->remove)
-                               dsim_lcd_drv->remove(dsim_ddi->dsim_lcd_dev);
-
-                       kfree(dsim_ddi);
-               }
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int exynos_mipi_dsi_suspend(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
-       struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
-       struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
-
-       disable_irq(dsim->irq);
-
-       if (dsim->suspended)
-               return 0;
-
-       if (client_drv && client_drv->suspend)
-               client_drv->suspend(client_dev);
-
-       /* disable MIPI-DSI PHY. */
-       phy_power_off(dsim->phy);
-
-       clk_disable(dsim->clock);
-
-       exynos_mipi_regulator_disable(dsim);
-
-       dsim->suspended = true;
-
-       return 0;
-}
-
-static int exynos_mipi_dsi_resume(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
-       struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
-       struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
-
-       enable_irq(dsim->irq);
-
-       if (!dsim->suspended)
-               return 0;
-
-       /* lcd panel power on. */
-       if (client_drv && client_drv->power_on)
-               client_drv->power_on(client_dev, 1);
-
-       exynos_mipi_regulator_enable(dsim);
-
-       /* enable MIPI-DSI PHY. */
-       phy_power_on(dsim->phy);
-
-       clk_enable(dsim->clock);
-
-       exynos_mipi_update_cfg(dsim);
-
-       /* set lcd panel sequence commands. */
-       if (client_drv && client_drv->set_sequence)
-               client_drv->set_sequence(client_dev);
-
-       dsim->suspended = false;
-
-       return 0;
-}
-#endif
-
-static const struct dev_pm_ops exynos_mipi_dsi_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(exynos_mipi_dsi_suspend, exynos_mipi_dsi_resume)
-};
-
-static struct platform_driver exynos_mipi_dsi_driver = {
-       .probe = exynos_mipi_dsi_probe,
-       .remove = exynos_mipi_dsi_remove,
-       .driver = {
-                  .name = "exynos-mipi-dsim",
-                  .pm = &exynos_mipi_dsi_pm_ops,
-       },
-};
-
-module_platform_driver(exynos_mipi_dsi_driver);
-
-MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC MIPI-DSI driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c
deleted file mode 100644 (file)
index 2358a2f..0000000
+++ /dev/null
@@ -1,880 +0,0 @@
-/* linux/drivers/video/exynos/exynos_mipi_dsi_common.c
- *
- * Samsung SoC MIPI-DSI common driver.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae, <inki.dae@samsung.com>
- * Donghwa Lee, <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/fb.h>
-#include <linux/ctype.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/memory.h>
-#include <linux/delay.h>
-#include <linux/irqreturn.h>
-#include <linux/kthread.h>
-
-#include <video/mipi_display.h>
-#include <video/exynos_mipi_dsim.h>
-
-#include "exynos_mipi_dsi_regs.h"
-#include "exynos_mipi_dsi_lowlevel.h"
-#include "exynos_mipi_dsi_common.h"
-
-#define MIPI_FIFO_TIMEOUT      msecs_to_jiffies(250)
-#define MIPI_RX_FIFO_READ_DONE  0x30800002
-#define MIPI_MAX_RX_FIFO        20
-#define MHZ                    (1000 * 1000)
-#define FIN_HZ                 (24 * MHZ)
-
-#define DFIN_PLL_MIN_HZ                (6 * MHZ)
-#define DFIN_PLL_MAX_HZ                (12 * MHZ)
-
-#define DFVCO_MIN_HZ           (500 * MHZ)
-#define DFVCO_MAX_HZ           (1000 * MHZ)
-
-#define TRY_GET_FIFO_TIMEOUT   (5000 * 2)
-#define TRY_FIFO_CLEAR         (10)
-
-/* MIPI-DSIM status types. */
-enum {
-       DSIM_STATE_INIT,        /* should be initialized. */
-       DSIM_STATE_STOP,        /* CPU and LCDC are LP mode. */
-       DSIM_STATE_HSCLKEN,     /* HS clock was enabled. */
-       DSIM_STATE_ULPS
-};
-
-/* define DSI lane types. */
-enum {
-       DSIM_LANE_CLOCK = (1 << 0),
-       DSIM_LANE_DATA0 = (1 << 1),
-       DSIM_LANE_DATA1 = (1 << 2),
-       DSIM_LANE_DATA2 = (1 << 3),
-       DSIM_LANE_DATA3 = (1 << 4)
-};
-
-static unsigned int dpll_table[15] = {
-       100, 120, 170, 220, 270,
-       320, 390, 450, 510, 560,
-       640, 690, 770, 870, 950
-};
-
-irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id)
-{
-       struct mipi_dsim_device *dsim = dev_id;
-       unsigned int intsrc, intmsk;
-
-       intsrc = exynos_mipi_dsi_read_interrupt(dsim);
-       intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim);
-       intmsk = ~intmsk & intsrc;
-
-       if (intsrc & INTMSK_RX_DONE) {
-               complete(&dsim_rd_comp);
-               dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n");
-       }
-       if (intsrc & INTMSK_FIFO_EMPTY) {
-               complete(&dsim_wr_comp);
-               dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n");
-       }
-
-       exynos_mipi_dsi_clear_interrupt(dsim, intmsk);
-
-       return IRQ_HANDLED;
-}
-
-/*
- * write long packet to mipi dsi slave
- * @dsim: mipi dsim device structure.
- * @data0: packet data to send.
- * @data1: size of packet data
- */
-static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim,
-               const unsigned char *data0, unsigned int data_size)
-{
-       unsigned int data_cnt = 0, payload = 0;
-
-       /* in case that data count is more then 4 */
-       for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) {
-               /*
-                * after sending 4bytes per one time,
-                * send remainder data less then 4.
-                */
-               if ((data_size - data_cnt) < 4) {
-                       if ((data_size - data_cnt) == 3) {
-                               payload = data0[data_cnt] |
-                                   data0[data_cnt + 1] << 8 |
-                                       data0[data_cnt + 2] << 16;
-                       dev_dbg(dsim->dev, "count = 3 payload = %x, %x %x %x\n",
-                               payload, data0[data_cnt],
-                               data0[data_cnt + 1],
-                               data0[data_cnt + 2]);
-                       } else if ((data_size - data_cnt) == 2) {
-                               payload = data0[data_cnt] |
-                                       data0[data_cnt + 1] << 8;
-                       dev_dbg(dsim->dev,
-                               "count = 2 payload = %x, %x %x\n", payload,
-                               data0[data_cnt],
-                               data0[data_cnt + 1]);
-                       } else if ((data_size - data_cnt) == 1) {
-                               payload = data0[data_cnt];
-                       }
-
-                       exynos_mipi_dsi_wr_tx_data(dsim, payload);
-               /* send 4bytes per one time. */
-               } else {
-                       payload = data0[data_cnt] |
-                               data0[data_cnt + 1] << 8 |
-                               data0[data_cnt + 2] << 16 |
-                               data0[data_cnt + 3] << 24;
-
-                       dev_dbg(dsim->dev,
-                               "count = 4 payload = %x, %x %x %x %x\n",
-                               payload, *(u8 *)(data0 + data_cnt),
-                               data0[data_cnt + 1],
-                               data0[data_cnt + 2],
-                               data0[data_cnt + 3]);
-
-                       exynos_mipi_dsi_wr_tx_data(dsim, payload);
-               }
-       }
-}
-
-int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
-       const unsigned char *data0, unsigned int data_size)
-{
-       unsigned int check_rx_ack = 0;
-
-       if (dsim->state == DSIM_STATE_ULPS) {
-               dev_err(dsim->dev, "state is ULPS.\n");
-
-               return -EINVAL;
-       }
-
-       /* FIXME!!! why does it need this delay? */
-       msleep(20);
-
-       mutex_lock(&dsim->lock);
-
-       switch (data_id) {
-       /* short packet types of packet types for command. */
-       case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
-       case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
-       case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
-       case MIPI_DSI_DCS_SHORT_WRITE:
-       case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
-       case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
-               exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
-               if (check_rx_ack) {
-                       /* process response func should be implemented */
-                       mutex_unlock(&dsim->lock);
-                       return 0;
-               } else {
-                       mutex_unlock(&dsim->lock);
-                       return -EINVAL;
-               }
-
-       /* general command */
-       case MIPI_DSI_COLOR_MODE_OFF:
-       case MIPI_DSI_COLOR_MODE_ON:
-       case MIPI_DSI_SHUTDOWN_PERIPHERAL:
-       case MIPI_DSI_TURN_ON_PERIPHERAL:
-               exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
-               if (check_rx_ack) {
-                       /* process response func should be implemented. */
-                       mutex_unlock(&dsim->lock);
-                       return 0;
-               } else {
-                       mutex_unlock(&dsim->lock);
-                       return -EINVAL;
-               }
-
-       /* packet types for video data */
-       case MIPI_DSI_V_SYNC_START:
-       case MIPI_DSI_V_SYNC_END:
-       case MIPI_DSI_H_SYNC_START:
-       case MIPI_DSI_H_SYNC_END:
-       case MIPI_DSI_END_OF_TRANSMISSION:
-               mutex_unlock(&dsim->lock);
-               return 0;
-
-       /* long packet type and null packet */
-       case MIPI_DSI_NULL_PACKET:
-       case MIPI_DSI_BLANKING_PACKET:
-               mutex_unlock(&dsim->lock);
-               return 0;
-       case MIPI_DSI_GENERIC_LONG_WRITE:
-       case MIPI_DSI_DCS_LONG_WRITE:
-       {
-               unsigned int size, payload = 0;
-               reinit_completion(&dsim_wr_comp);
-
-               size = data_size * 4;
-
-               /* if data count is less then 4, then send 3bytes data.  */
-               if (data_size < 4) {
-                       payload = data0[0] |
-                               data0[1] << 8 |
-                               data0[2] << 16;
-
-                       exynos_mipi_dsi_wr_tx_data(dsim, payload);
-
-                       dev_dbg(dsim->dev, "count = %d payload = %x,%x %x %x\n",
-                               data_size, payload, data0[0],
-                               data0[1], data0[2]);
-
-               /* in case that data count is more then 4 */
-               } else
-                       exynos_mipi_dsi_long_data_wr(dsim, data0, data_size);
-
-               /* put data into header fifo */
-               exynos_mipi_dsi_wr_tx_header(dsim, data_id, data_size & 0xff,
-                       (data_size & 0xff00) >> 8);
-
-               if (!wait_for_completion_interruptible_timeout(&dsim_wr_comp,
-                                                       MIPI_FIFO_TIMEOUT)) {
-                       dev_warn(dsim->dev, "command write timeout.\n");
-                       mutex_unlock(&dsim->lock);
-                       return -EAGAIN;
-               }
-
-               if (check_rx_ack) {
-                       /* process response func should be implemented. */
-                       mutex_unlock(&dsim->lock);
-                       return 0;
-               } else {
-                       mutex_unlock(&dsim->lock);
-                       return -EINVAL;
-               }
-       }
-
-       /* packet typo for video data */
-       case MIPI_DSI_PACKED_PIXEL_STREAM_16:
-       case MIPI_DSI_PACKED_PIXEL_STREAM_18:
-       case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
-       case MIPI_DSI_PACKED_PIXEL_STREAM_24:
-               if (check_rx_ack) {
-                       /* process response func should be implemented. */
-                       mutex_unlock(&dsim->lock);
-                       return 0;
-               } else {
-                       mutex_unlock(&dsim->lock);
-                       return -EINVAL;
-               }
-       default:
-               dev_warn(dsim->dev,
-                       "data id %x is not supported current DSI spec.\n",
-                       data_id);
-
-               mutex_unlock(&dsim->lock);
-               return -EINVAL;
-       }
-}
-
-static unsigned int exynos_mipi_dsi_long_data_rd(struct mipi_dsim_device *dsim,
-               unsigned int req_size, unsigned int rx_data, u8 *rx_buf)
-{
-       unsigned int rcv_pkt, i, j;
-       u16 rxsize;
-
-       /* for long packet */
-       rxsize = (u16)((rx_data & 0x00ffff00) >> 8);
-       dev_dbg(dsim->dev, "mipi dsi rx size : %d\n", rxsize);
-       if (rxsize != req_size) {
-               dev_dbg(dsim->dev,
-                       "received size mismatch received: %d, requested: %d\n",
-                       rxsize, req_size);
-               goto err;
-       }
-
-       for (i = 0; i < (rxsize >> 2); i++) {
-               rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
-               dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt);
-               for (j = 0; j < 4; j++) {
-                       rx_buf[(i * 4) + j] =
-                                       (u8)(rcv_pkt >> (j * 8)) & 0xff;
-                       dev_dbg(dsim->dev, "received value : %02x\n",
-                                       (rcv_pkt >> (j * 8)) & 0xff);
-               }
-       }
-       if (rxsize % 4) {
-               rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
-               dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt);
-               for (j = 0; j < (rxsize % 4); j++) {
-                       rx_buf[(i * 4) + j] =
-                                       (u8)(rcv_pkt >> (j * 8)) & 0xff;
-                       dev_dbg(dsim->dev, "received value : %02x\n",
-                                       (rcv_pkt >> (j * 8)) & 0xff);
-               }
-       }
-
-       return rxsize;
-
-err:
-       return -EINVAL;
-}
-
-static unsigned int exynos_mipi_dsi_response_size(unsigned int req_size)
-{
-       switch (req_size) {
-       case 1:
-               return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE;
-       case 2:
-               return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE;
-       default:
-               return MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE;
-       }
-}
-
-int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id,
-       unsigned int data0, unsigned int req_size, u8 *rx_buf)
-{
-       unsigned int rx_data, rcv_pkt, i;
-       u8 response = 0;
-       u16 rxsize;
-
-       if (dsim->state == DSIM_STATE_ULPS) {
-               dev_err(dsim->dev, "state is ULPS.\n");
-
-               return -EINVAL;
-       }
-
-       /* FIXME!!! */
-       msleep(20);
-
-       mutex_lock(&dsim->lock);
-       reinit_completion(&dsim_rd_comp);
-       exynos_mipi_dsi_rd_tx_header(dsim,
-               MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, req_size);
-
-       response = exynos_mipi_dsi_response_size(req_size);
-
-       switch (data_id) {
-       case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
-       case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
-       case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
-       case MIPI_DSI_DCS_READ:
-               exynos_mipi_dsi_rd_tx_header(dsim,
-                       data_id, data0);
-               /* process response func should be implemented. */
-               break;
-       default:
-               dev_warn(dsim->dev,
-                       "data id %x is not supported current DSI spec.\n",
-                       data_id);
-
-               mutex_unlock(&dsim->lock);
-               return -EINVAL;
-       }
-
-       if (!wait_for_completion_interruptible_timeout(&dsim_rd_comp,
-                               MIPI_FIFO_TIMEOUT)) {
-               pr_err("RX done interrupt timeout\n");
-               mutex_unlock(&dsim->lock);
-               return 0;
-       }
-
-       msleep(20);
-
-       rx_data = exynos_mipi_dsi_rd_rx_fifo(dsim);
-
-       if ((u8)(rx_data & 0xff) != response) {
-               printk(KERN_ERR
-                       "mipi dsi wrong response rx_data : %x, response:%x\n",
-                       rx_data, response);
-               goto clear_rx_fifo;
-       }
-
-       if (req_size <= 2) {
-               /* for short packet */
-               for (i = 0; i < req_size; i++)
-                       rx_buf[i] = (rx_data >> (8 + (i * 8))) & 0xff;
-               rxsize = req_size;
-       } else {
-               /* for long packet */
-               rxsize = exynos_mipi_dsi_long_data_rd(dsim, req_size, rx_data,
-                                                       rx_buf);
-               if (rxsize != req_size)
-                       goto clear_rx_fifo;
-       }
-
-       rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
-
-       msleep(20);
-
-       if (rcv_pkt != MIPI_RX_FIFO_READ_DONE) {
-               dev_info(dsim->dev,
-                       "Can't found RX FIFO READ DONE FLAG : %x\n", rcv_pkt);
-               goto clear_rx_fifo;
-       }
-
-       mutex_unlock(&dsim->lock);
-
-       return rxsize;
-
-clear_rx_fifo:
-       i = 0;
-       while (1) {
-               rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
-               if ((rcv_pkt == MIPI_RX_FIFO_READ_DONE)
-                               || (i > MIPI_MAX_RX_FIFO))
-                       break;
-               dev_dbg(dsim->dev,
-                               "mipi dsi clear rx fifo : %08x\n", rcv_pkt);
-               i++;
-       }
-       dev_info(dsim->dev,
-               "mipi dsi rx done count : %d, rcv_pkt : %08x\n", i, rcv_pkt);
-
-       mutex_unlock(&dsim->lock);
-
-       return 0;
-}
-
-static int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim,
-                               unsigned int enable)
-{
-       int sw_timeout;
-
-       if (enable) {
-               sw_timeout = 1000;
-
-               exynos_mipi_dsi_enable_pll(dsim, 1);
-               while (1) {
-                       sw_timeout--;
-                       if (exynos_mipi_dsi_is_pll_stable(dsim))
-                               return 0;
-                       if (sw_timeout == 0)
-                               return -EINVAL;
-               }
-       } else
-               exynos_mipi_dsi_enable_pll(dsim, 0);
-
-       return 0;
-}
-
-static unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim,
-       unsigned int pre_divider, unsigned int main_divider,
-       unsigned int scaler)
-{
-       unsigned long dfin_pll, dfvco, dpll_out;
-       unsigned int i, freq_band = 0xf;
-
-       dfin_pll = (FIN_HZ / pre_divider);
-
-       /******************************************************
-        *      Serial Clock(=ByteClk X 8)      FreqBand[3:0] *
-        ******************************************************
-        *      ~ 99.99 MHz                     0000
-        *      100 ~ 119.99 MHz                0001
-        *      120 ~ 159.99 MHz                0010
-        *      160 ~ 199.99 MHz                0011
-        *      200 ~ 239.99 MHz                0100
-        *      140 ~ 319.99 MHz                0101
-        *      320 ~ 389.99 MHz                0110
-        *      390 ~ 449.99 MHz                0111
-        *      450 ~ 509.99 MHz                1000
-        *      510 ~ 559.99 MHz                1001
-        *      560 ~ 639.99 MHz                1010
-        *      640 ~ 689.99 MHz                1011
-        *      690 ~ 769.99 MHz                1100
-        *      770 ~ 869.99 MHz                1101
-        *      870 ~ 949.99 MHz                1110
-        *      950 ~ 1000 MHz                  1111
-        ******************************************************/
-       if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) {
-               dev_warn(dsim->dev, "fin_pll range should be 6MHz ~ 12MHz\n");
-               exynos_mipi_dsi_enable_afc(dsim, 0, 0);
-       } else {
-               if (dfin_pll < 7 * MHZ)
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x1);
-               else if (dfin_pll < 8 * MHZ)
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x0);
-               else if (dfin_pll < 9 * MHZ)
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x3);
-               else if (dfin_pll < 10 * MHZ)
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x2);
-               else if (dfin_pll < 11 * MHZ)
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x5);
-               else
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x4);
-       }
-
-       dfvco = dfin_pll * main_divider;
-       dev_dbg(dsim->dev, "dfvco = %lu, dfin_pll = %lu, main_divider = %d\n",
-                               dfvco, dfin_pll, main_divider);
-       if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ)
-               dev_warn(dsim->dev, "fvco range should be 500MHz ~ 1000MHz\n");
-
-       dpll_out = dfvco / (1 << scaler);
-       dev_dbg(dsim->dev, "dpll_out = %lu, dfvco = %lu, scaler = %d\n",
-               dpll_out, dfvco, scaler);
-
-       for (i = 0; i < ARRAY_SIZE(dpll_table); i++) {
-               if (dpll_out < dpll_table[i] * MHZ) {
-                       freq_band = i;
-                       break;
-               }
-       }
-
-       dev_dbg(dsim->dev, "freq_band = %d\n", freq_band);
-
-       exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler);
-
-       exynos_mipi_dsi_hs_zero_ctrl(dsim, 0);
-       exynos_mipi_dsi_prep_ctrl(dsim, 0);
-
-       /* Freq Band */
-       exynos_mipi_dsi_pll_freq_band(dsim, freq_band);
-
-       /* Stable time */
-       exynos_mipi_dsi_pll_stable_time(dsim, dsim->dsim_config->pll_stable_time);
-
-       /* Enable PLL */
-       dev_dbg(dsim->dev, "FOUT of mipi dphy pll is %luMHz\n",
-               (dpll_out / MHZ));
-
-       return dpll_out;
-}
-
-static int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim,
-       unsigned int byte_clk_sel, unsigned int enable)
-{
-       unsigned int esc_div;
-       unsigned long esc_clk_error_rate;
-       unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0;
-
-       if (enable) {
-               dsim->e_clk_src = byte_clk_sel;
-
-               /* Escape mode clock and byte clock source */
-               exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel);
-
-               /* DPHY, DSIM Link : D-PHY clock out */
-               if (byte_clk_sel == DSIM_PLL_OUT_DIV8) {
-                       hs_clk = exynos_mipi_dsi_change_pll(dsim,
-                               dsim->dsim_config->p, dsim->dsim_config->m,
-                               dsim->dsim_config->s);
-                       if (hs_clk == 0) {
-                               dev_err(dsim->dev,
-                                       "failed to get hs clock.\n");
-                               return -EINVAL;
-                       }
-
-                       byte_clk = hs_clk / 8;
-                       exynos_mipi_dsi_enable_pll_bypass(dsim, 0);
-                       exynos_mipi_dsi_pll_on(dsim, 1);
-               /* DPHY : D-PHY clock out, DSIM link : external clock out */
-               } else if (byte_clk_sel == DSIM_EXT_CLK_DIV8) {
-                       dev_warn(dsim->dev, "this project is not support\n");
-                       dev_warn(dsim->dev,
-                               "external clock source for MIPI DSIM.\n");
-               } else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS) {
-                       dev_warn(dsim->dev, "this project is not support\n");
-                       dev_warn(dsim->dev,
-                               "external clock source for MIPI DSIM\n");
-               }
-
-               /* escape clock divider */
-               esc_div = byte_clk / (dsim->dsim_config->esc_clk);
-               dev_dbg(dsim->dev,
-                       "esc_div = %d, byte_clk = %lu, esc_clk = %lu\n",
-                       esc_div, byte_clk, dsim->dsim_config->esc_clk);
-               if ((byte_clk / esc_div) >= (20 * MHZ) ||
-                               (byte_clk / esc_div) >
-                                       dsim->dsim_config->esc_clk)
-                       esc_div += 1;
-
-               escape_clk = byte_clk / esc_div;
-               dev_dbg(dsim->dev,
-                       "escape_clk = %lu, byte_clk = %lu, esc_div = %d\n",
-                       escape_clk, byte_clk, esc_div);
-
-               /* enable escape clock. */
-               exynos_mipi_dsi_enable_byte_clock(dsim, 1);
-
-               /* enable byte clk and escape clock */
-               exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div);
-               /* escape clock on lane */
-               exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
-                       (DSIM_LANE_CLOCK | dsim->data_lane), 1);
-
-               dev_dbg(dsim->dev, "byte clock is %luMHz\n",
-                       (byte_clk / MHZ));
-               dev_dbg(dsim->dev, "escape clock that user's need is %lu\n",
-                       (dsim->dsim_config->esc_clk / MHZ));
-               dev_dbg(dsim->dev, "escape clock divider is %x\n", esc_div);
-               dev_dbg(dsim->dev, "escape clock is %luMHz\n",
-                       ((byte_clk / esc_div) / MHZ));
-
-               if ((byte_clk / esc_div) > escape_clk) {
-                       esc_clk_error_rate = escape_clk /
-                               (byte_clk / esc_div);
-                       dev_warn(dsim->dev, "error rate is %lu over.\n",
-                               (esc_clk_error_rate / 100));
-               } else if ((byte_clk / esc_div) < (escape_clk)) {
-                       esc_clk_error_rate = (byte_clk / esc_div) /
-                               escape_clk;
-                       dev_warn(dsim->dev, "error rate is %lu under.\n",
-                               (esc_clk_error_rate / 100));
-               }
-       } else {
-               exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
-                       (DSIM_LANE_CLOCK | dsim->data_lane), 0);
-               exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0);
-
-               /* disable escape clock. */
-               exynos_mipi_dsi_enable_byte_clock(dsim, 0);
-
-               if (byte_clk_sel == DSIM_PLL_OUT_DIV8)
-                       exynos_mipi_dsi_pll_on(dsim, 0);
-       }
-
-       return 0;
-}
-
-int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim)
-{
-       dsim->state = DSIM_STATE_INIT;
-
-       switch (dsim->dsim_config->e_no_data_lane) {
-       case DSIM_DATA_LANE_1:
-               dsim->data_lane = DSIM_LANE_DATA0;
-               break;
-       case DSIM_DATA_LANE_2:
-               dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1;
-               break;
-       case DSIM_DATA_LANE_3:
-               dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
-                       DSIM_LANE_DATA2;
-               break;
-       case DSIM_DATA_LANE_4:
-               dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
-                       DSIM_LANE_DATA2 | DSIM_LANE_DATA3;
-               break;
-       default:
-               dev_info(dsim->dev, "data lane is invalid.\n");
-               return -EINVAL;
-       }
-
-       exynos_mipi_dsi_sw_reset(dsim);
-       exynos_mipi_dsi_func_reset(dsim);
-
-       exynos_mipi_dsi_dp_dn_swap(dsim, 0);
-
-       return 0;
-}
-
-void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim)
-{
-       unsigned int src = 0;
-
-       src = (INTSRC_SFR_FIFO_EMPTY | INTSRC_RX_DATA_DONE);
-       exynos_mipi_dsi_set_interrupt(dsim, src, 1);
-
-       src = 0;
-       src = ~(INTMSK_RX_DONE | INTMSK_FIFO_EMPTY);
-       exynos_mipi_dsi_set_interrupt_mask(dsim, src, 1);
-}
-
-int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
-       unsigned int enable)
-{
-       /* enable only frame done interrupt */
-       exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable);
-
-       return 0;
-}
-
-void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim,
-               unsigned int enable)
-{
-
-       /* consider Main display and Sub display. */
-
-       exynos_mipi_dsi_set_main_stand_by(dsim, enable);
-}
-
-int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
-       struct mipi_dsim_config *dsim_config)
-{
-       struct mipi_dsim_platform_data *dsim_pd;
-       struct fb_videomode *timing;
-
-       dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd;
-       timing = (struct fb_videomode *)dsim_pd->lcd_panel_info;
-
-       /* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */
-       if (dsim_config->e_interface == (u32) DSIM_VIDEO) {
-               if (dsim_config->auto_vertical_cnt == 0) {
-                       exynos_mipi_dsi_set_main_disp_vporch(dsim,
-                               dsim_config->cmd_allow,
-                               timing->lower_margin,
-                               timing->upper_margin);
-                       exynos_mipi_dsi_set_main_disp_hporch(dsim,
-                               timing->right_margin,
-                               timing->left_margin);
-                       exynos_mipi_dsi_set_main_disp_sync_area(dsim,
-                               timing->vsync_len,
-                               timing->hsync_len);
-               }
-       }
-
-       exynos_mipi_dsi_set_main_disp_resol(dsim, timing->xres,
-                       timing->yres);
-
-       exynos_mipi_dsi_display_config(dsim, dsim_config);
-
-       dev_info(dsim->dev, "lcd panel ==> width = %d, height = %d\n",
-                       timing->xres, timing->yres);
-
-       return 0;
-}
-
-int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim)
-{
-       unsigned int time_out = 100;
-
-       switch (dsim->state) {
-       case DSIM_STATE_INIT:
-               exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f);
-
-               /* dsi configuration */
-               exynos_mipi_dsi_init_config(dsim);
-               exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1);
-               exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1);
-
-               /* set clock configuration */
-               exynos_mipi_dsi_set_clock(dsim, dsim->dsim_config->e_byte_clk, 1);
-
-               /* check clock and data lane state are stop state */
-               while (!(exynos_mipi_dsi_is_lane_state(dsim))) {
-                       time_out--;
-                       if (time_out == 0) {
-                               dev_err(dsim->dev,
-                                       "DSI Master is not stop state.\n");
-                               dev_err(dsim->dev,
-                                       "Check initialization process\n");
-
-                               return -EINVAL;
-                       }
-               }
-               if (time_out != 0) {
-                       dev_info(dsim->dev,
-                               "DSI Master driver has been completed.\n");
-                       dev_info(dsim->dev, "DSI Master state is stop state\n");
-               }
-
-               dsim->state = DSIM_STATE_STOP;
-
-               /* BTA sequence counters */
-               exynos_mipi_dsi_set_stop_state_counter(dsim,
-                       dsim->dsim_config->stop_holding_cnt);
-               exynos_mipi_dsi_set_bta_timeout(dsim,
-                       dsim->dsim_config->bta_timeout);
-               exynos_mipi_dsi_set_lpdr_timeout(dsim,
-                       dsim->dsim_config->rx_timeout);
-
-               return 0;
-       default:
-               dev_info(dsim->dev, "DSI Master is already init.\n");
-               return 0;
-       }
-
-       return 0;
-}
-
-int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim)
-{
-       if (dsim->state != DSIM_STATE_STOP) {
-               dev_warn(dsim->dev, "DSIM is not in stop state.\n");
-               return 0;
-       }
-
-       if (dsim->e_clk_src == DSIM_EXT_CLK_BYPASS) {
-               dev_warn(dsim->dev, "clock source is external bypass.\n");
-               return 0;
-       }
-
-       dsim->state = DSIM_STATE_HSCLKEN;
-
-        /* set LCDC and CPU transfer mode to HS. */
-       exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
-       exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
-       exynos_mipi_dsi_enable_hs_clock(dsim, 1);
-
-       return 0;
-}
-
-int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
-               unsigned int mode)
-{
-       if (mode) {
-               if (dsim->state != DSIM_STATE_HSCLKEN) {
-                       dev_err(dsim->dev, "HS Clock lane is not enabled.\n");
-                       return -EINVAL;
-               }
-
-               exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
-       } else {
-               if (dsim->state == DSIM_STATE_INIT || dsim->state ==
-                       DSIM_STATE_ULPS) {
-                       dev_err(dsim->dev,
-                               "DSI Master is not STOP or HSDT state.\n");
-                       return -EINVAL;
-               }
-
-               exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
-       }
-
-       return 0;
-}
-
-int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim)
-{
-       return _exynos_mipi_dsi_get_frame_done_status(dsim);
-}
-
-int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
-{
-       _exynos_mipi_dsi_clear_frame_done(dsim);
-
-       return 0;
-}
-
-int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim,
-                               unsigned int val)
-{
-       int try = TRY_FIFO_CLEAR;
-
-       exynos_mipi_dsi_sw_reset_release(dsim);
-       exynos_mipi_dsi_func_reset(dsim);
-
-       do {
-               if (exynos_mipi_dsi_get_sw_reset_release(dsim)) {
-                       exynos_mipi_dsi_init_interrupt(dsim);
-                       dev_dbg(dsim->dev, "reset release done.\n");
-                       return 0;
-               }
-       } while (--try);
-
-       dev_err(dsim->dev, "failed to clear dsim fifo.\n");
-       return -EAGAIN;
-}
-
-MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC MIPI-DSI common driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h
deleted file mode 100644 (file)
index 4125522..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* linux/drivers/video/exynos_mipi_dsi_common.h
- *
- * Header file for Samsung SoC MIPI-DSI common driver.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.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 _EXYNOS_MIPI_DSI_COMMON_H
-#define _EXYNOS_MIPI_DSI_COMMON_H
-
-static DECLARE_COMPLETION(dsim_rd_comp);
-static DECLARE_COMPLETION(dsim_wr_comp);
-
-int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
-       const unsigned char *data0, unsigned int data_size);
-int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id,
-       unsigned int data0, unsigned int req_size, u8 *rx_buf);
-irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id);
-void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim);
-int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim,
-               unsigned int enable);
-int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
-                       struct mipi_dsim_config *dsim_info);
-int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim);
-int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim);
-int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
-               unsigned int mode);
-int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
-       unsigned int enable);
-int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim);
-int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
-
-extern struct fb_info *registered_fb[FB_MAX] __read_mostly;
-
-int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim,
-                               unsigned int val);
-
-#endif /* _EXYNOS_MIPI_DSI_COMMON_H */
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c
deleted file mode 100644 (file)
index c148d06..0000000
+++ /dev/null
@@ -1,618 +0,0 @@
-/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c
- *
- * Samsung SoC MIPI-DSI lowlevel driver.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae, <inki.dae@samsung.com>
- * Donghwa Lee, <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/ctype.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-
-#include <video/exynos_mipi_dsim.h>
-
-#include "exynos_mipi_dsi_regs.h"
-#include "exynos_mipi_dsi_lowlevel.h"
-
-void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST);
-
-       reg |= DSIM_FUNCRST;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST);
-}
-
-void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST);
-
-       reg |= DSIM_SWRST;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST);
-}
-
-void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
-
-       reg |= INTSRC_SW_RST_RELEASE;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC);
-}
-
-int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim)
-{
-       return (readl(dsim->reg_base + EXYNOS_DSIM_INTSRC)) &
-                       INTSRC_SW_RST_RELEASE;
-}
-
-unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_INTMSK);
-
-       return reg;
-}
-
-void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
-               unsigned int mode, unsigned int mask)
-{
-       unsigned int reg = 0;
-
-       if (mask)
-               reg |= mode;
-       else
-               reg &= ~mode;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_INTMSK);
-}
-
-void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
-               unsigned int cfg)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL);
-
-       writel(reg & ~(cfg), dsim->reg_base + EXYNOS_DSIM_FIFOCTRL);
-       mdelay(10);
-       reg |= cfg;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_FIFOCTRL);
-}
-
-/*
- * this function set PLL P, M and S value in D-PHY
- */
-void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
-               unsigned int value)
-{
-       writel(DSIM_AFC_CTL(value), dsim->reg_base + EXYNOS_DSIM_PHYACCHR);
-}
-
-void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim,
-               unsigned int enable)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL);
-
-       reg &= ~DSIM_MAIN_STAND_BY;
-
-       if (enable)
-               reg |= DSIM_MAIN_STAND_BY;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL);
-}
-
-void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
-       unsigned int width_resol, unsigned int height_resol)
-{
-       unsigned int reg;
-
-       /* standby should be set after configuration so set to not ready*/
-       reg = (readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL)) &
-               ~(DSIM_MAIN_STAND_BY);
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL);
-
-       reg &= ~((0x7ff << 16) | (0x7ff << 0));
-       reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol);
-
-       reg |= DSIM_MAIN_STAND_BY;
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL);
-}
-
-void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
-       unsigned int cmd_allow, unsigned int vfront, unsigned int vback)
-{
-       unsigned int reg;
-
-       reg = (readl(dsim->reg_base + EXYNOS_DSIM_MVPORCH)) &
-               ~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) |
-               (DSIM_MAIN_VBP_MASK));
-
-       reg |= (DSIM_CMD_ALLOW_SHIFT(cmd_allow & 0xf) |
-               DSIM_STABLE_VFP_SHIFT(vfront & 0x7ff) |
-               DSIM_MAIN_VBP_SHIFT(vback & 0x7ff));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MVPORCH);
-}
-
-void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
-       unsigned int front, unsigned int back)
-{
-       unsigned int reg;
-
-       reg = (readl(dsim->reg_base + EXYNOS_DSIM_MHPORCH)) &
-               ~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK));
-
-       reg |= DSIM_MAIN_HFP_SHIFT(front) | DSIM_MAIN_HBP_SHIFT(back);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MHPORCH);
-}
-
-void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
-       unsigned int vert, unsigned int hori)
-{
-       unsigned int reg;
-
-       reg = (readl(dsim->reg_base + EXYNOS_DSIM_MSYNC)) &
-               ~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK));
-
-       reg |= (DSIM_MAIN_VSA_SHIFT(vert & 0x3ff) |
-               DSIM_MAIN_HSA_SHIFT(hori));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MSYNC);
-}
-
-void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
-       unsigned int vert, unsigned int hori)
-{
-       unsigned int reg;
-
-       reg = (readl(dsim->reg_base + EXYNOS_DSIM_SDRESOL)) &
-               ~(DSIM_SUB_STANDY_MASK);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL);
-
-       reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK);
-       reg |= (DSIM_SUB_VRESOL_SHIFT(vert & 0x7ff) |
-               DSIM_SUB_HRESOL_SHIFT(hori & 0x7ff));
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL);
-
-       reg |= DSIM_SUB_STANDY_SHIFT(1);
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL);
-}
-
-void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim)
-{
-       struct mipi_dsim_config *dsim_config = dsim->dsim_config;
-
-       unsigned int cfg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) &
-               ~((1 << 28) | (0x1f << 20) | (0x3 << 5));
-
-       cfg =   ((DSIM_AUTO_FLUSH(dsim_config->auto_flush)) |
-               (DSIM_EOT_DISABLE(dsim_config->eot_disable)) |
-               (DSIM_AUTO_MODE_SHIFT(dsim_config->auto_vertical_cnt)) |
-               (DSIM_HSE_MODE_SHIFT(dsim_config->hse)) |
-               (DSIM_HFP_MODE_SHIFT(dsim_config->hfp)) |
-               (DSIM_HBP_MODE_SHIFT(dsim_config->hbp)) |
-               (DSIM_HSA_MODE_SHIFT(dsim_config->hsa)) |
-               (DSIM_NUM_OF_DATALANE_SHIFT(dsim_config->e_no_data_lane)));
-
-       writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
-}
-
-void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
-                               struct mipi_dsim_config *dsim_config)
-{
-       u32 reg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) &
-               ~((0x3 << 26) | (1 << 25) | (0x3 << 18) | (0x7 << 12) |
-               (0x3 << 16) | (0x7 << 8));
-
-       if (dsim_config->e_interface == DSIM_VIDEO)
-               reg |= (1 << 25);
-       else if (dsim_config->e_interface == DSIM_COMMAND)
-               reg &= ~(1 << 25);
-       else {
-               dev_err(dsim->dev, "unknown lcd type.\n");
-               return;
-       }
-
-       /* main lcd */
-       reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << 26 |
-               ((u8) (dsim_config->e_virtual_ch) & 0x3) << 18 |
-               ((u8) (dsim_config->e_pixel_format) & 0x7) << 12;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
-}
-
-void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane,
-       unsigned int enable)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_CONFIG);
-
-       if (enable)
-               reg |= DSIM_LANE_ENx(lane);
-       else
-               reg &= ~DSIM_LANE_ENx(lane);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
-}
-
-
-void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
-       unsigned int count)
-{
-       unsigned int cfg;
-
-       /* get the data lane number. */
-       cfg = DSIM_NUM_OF_DATALANE_SHIFT(count);
-
-       writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
-}
-
-void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable,
-       unsigned int afc_code)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR);
-
-       if (enable) {
-               reg |= (1 << 14);
-               reg &= ~(0x7 << 5);
-               reg |= (afc_code & 0x7) << 5;
-       } else
-               reg &= ~(1 << 14);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR);
-}
-
-void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
-       unsigned int enable)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
-               ~(DSIM_PLL_BYPASS_SHIFT(0x1));
-
-       reg |= DSIM_PLL_BYPASS_SHIFT(enable);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p,
-       unsigned int m, unsigned int s)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-
-       reg |= ((p & 0x3f) << 13) | ((m & 0x1ff) << 4) | ((s & 0x7) << 1);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
-               unsigned int freq_band)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
-               ~(DSIM_FREQ_BAND_SHIFT(0x1f));
-
-       reg |= DSIM_FREQ_BAND_SHIFT(freq_band & 0x1f);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
-               unsigned int pre_divider, unsigned int main_divider,
-               unsigned int scaler)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
-               ~(0x7ffff << 1);
-
-       reg |= (pre_divider & 0x3f) << 13 | (main_divider & 0x1ff) << 4 |
-               (scaler & 0x7) << 1;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
-       unsigned int lock_time)
-{
-       writel(lock_time, dsim->reg_base + EXYNOS_DSIM_PLLTMR);
-}
-
-void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, unsigned int enable)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
-               ~(DSIM_PLL_EN_SHIFT(0x1));
-
-       reg |= DSIM_PLL_EN_SHIFT(enable & 0x1);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
-               unsigned int src)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
-               ~(DSIM_BYTE_CLK_SRC_SHIFT(0x3));
-
-       reg |= (DSIM_BYTE_CLK_SRC_SHIFT(src));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
-               unsigned int enable)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
-               ~(DSIM_BYTE_CLKEN_SHIFT(0x1));
-
-       reg |= DSIM_BYTE_CLKEN_SHIFT(enable);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
-               unsigned int enable, unsigned int prs_val)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
-               ~(DSIM_ESC_CLKEN_SHIFT(0x1) | 0xffff);
-
-       reg |= DSIM_ESC_CLKEN_SHIFT(enable);
-       if (enable)
-               reg |= prs_val;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
-               unsigned int lane_sel, unsigned int enable)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-
-       if (enable)
-               reg |= DSIM_LANE_ESC_CLKEN(lane_sel);
-       else
-
-               reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
-       unsigned int enable)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) &
-               ~(DSIM_FORCE_STOP_STATE_SHIFT(0x1));
-
-       reg |= (DSIM_FORCE_STOP_STATE_SHIFT(enable & 0x1));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-}
-
-unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS);
-
-       /**
-        * check clock and data lane states.
-        * if MIPI-DSI controller was enabled at bootloader then
-        * TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK.
-        * so it should be checked for two case.
-        */
-       if ((reg & DSIM_STOP_STATE_DAT(0xf)) &&
-                       ((reg & DSIM_STOP_STATE_CLK) ||
-                        (reg & DSIM_TX_READY_HS_CLK)))
-               return 1;
-
-       return 0;
-}
-
-void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
-               unsigned int cnt_val)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) &
-               ~(DSIM_STOP_STATE_CNT_SHIFT(0x7ff));
-
-       reg |= (DSIM_STOP_STATE_CNT_SHIFT(cnt_val & 0x7ff));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-}
-
-void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
-               unsigned int timeout)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) &
-               ~(DSIM_BTA_TOUT_SHIFT(0xff));
-
-       reg |= (DSIM_BTA_TOUT_SHIFT(timeout));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT);
-}
-
-void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
-               unsigned int timeout)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) &
-               ~(DSIM_LPDR_TOUT_SHIFT(0xffff));
-
-       reg |= (DSIM_LPDR_TOUT_SHIFT(timeout));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT);
-}
-
-void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
-               unsigned int lp)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-
-       reg &= ~DSIM_CMD_LPDT_LP;
-
-       if (lp)
-               reg |= DSIM_CMD_LPDT_LP;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-}
-
-void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
-               unsigned int lp)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-
-       reg &= ~DSIM_TX_LPDT_LP;
-
-       if (lp)
-               reg |= DSIM_TX_LPDT_LP;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-}
-
-void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
-               unsigned int enable)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
-               ~(DSIM_TX_REQUEST_HSCLK_SHIFT(0x1));
-
-       reg |= DSIM_TX_REQUEST_HSCLK_SHIFT(enable);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
-               unsigned int swap_en)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR1);
-
-       reg &= ~(0x3 << 0);
-       reg |= (swap_en & 0x3) << 0;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR1);
-}
-
-void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
-               unsigned int hs_zero)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
-               ~(0xf << 28);
-
-       reg |= ((hs_zero & 0xf) << 28);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
-               ~(0x7 << 20);
-
-       reg |= ((prep & 0x7) << 20);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim)
-{
-       return readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
-}
-
-void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim,
-                                       unsigned int src)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
-
-       reg |= src;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC);
-}
-
-void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim,
-                                       unsigned int src, unsigned int enable)
-{
-       unsigned int reg = 0;
-
-       if (enable)
-               reg |= src;
-       else
-               reg &= ~src;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC);
-}
-
-unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS);
-
-       return reg & (1 << 31) ? 1 : 0;
-}
-
-unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim)
-{
-       return readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL) & ~(0x1f);
-}
-
-void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim,
-       unsigned int di, unsigned int data0, unsigned int data1)
-{
-       unsigned int reg = (data1 << 16) | (data0 << 8) | ((di & 0x3f) << 0);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR);
-}
-
-void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim,
-       unsigned int di, unsigned int data0)
-{
-       unsigned int reg = (data0 << 8) | (di << 0);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR);
-}
-
-unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim)
-{
-       return readl(dsim->reg_base + EXYNOS_DSIM_RXFIFO);
-}
-
-unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
-
-       return (reg & INTSRC_FRAME_DONE) ? 1 : 0;
-}
-
-void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
-
-       writel(reg | INTSRC_FRAME_DONE, dsim->reg_base +
-               EXYNOS_DSIM_INTSRC);
-}
-
-void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
-               unsigned int tx_data)
-{
-       writel(tx_data, dsim->reg_base + EXYNOS_DSIM_PAYLOAD);
-}
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h
deleted file mode 100644 (file)
index 8546070..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h
- *
- * Header file for Samsung SoC MIPI-DSI lowlevel driver.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.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 _EXYNOS_MIPI_DSI_LOWLEVEL_H
-#define _EXYNOS_MIPI_DSI_LOWLEVEL_H
-
-void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim);
-int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
-       unsigned int mode, unsigned int mask);
-void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
-                                       unsigned int count);
-void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
-                                       unsigned int cfg);
-void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
-                               unsigned int value);
-void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
-                               unsigned int value);
-void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim,
-               unsigned int enable);
-void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
-               unsigned int width_resol, unsigned int height_resol);
-void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
-       unsigned int cmd_allow, unsigned int vfront, unsigned int vback);
-void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
-                       unsigned int front, unsigned int back);
-void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
-                               unsigned int vert, unsigned int hori);
-void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
-                               unsigned int vert, unsigned int hori);
-void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
-                               struct mipi_dsim_config *dsim_config);
-void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
-                               unsigned int count);
-void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane,
-                               unsigned int enable);
-void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable,
-                               unsigned int afc_code);
-void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
-                               unsigned int enable);
-void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p,
-                               unsigned int m, unsigned int s);
-void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
-                               unsigned int freq_band);
-void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
-                       unsigned int pre_divider, unsigned int main_divider,
-                       unsigned int scaler);
-void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
-                       unsigned int lock_time);
-void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim,
-                                       unsigned int enable);
-void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
-                                       unsigned int src);
-void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
-                                       unsigned int enable);
-void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
-                               unsigned int enable, unsigned int prs_val);
-void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
-                               unsigned int lane_sel, unsigned int enable);
-void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
-                               unsigned int enable);
-unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
-                               unsigned int cnt_val);
-void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
-                               unsigned int timeout);
-void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
-                               unsigned int timeout);
-void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
-                                       unsigned int lp);
-void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
-                                       unsigned int lp);
-void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
-                               unsigned int enable);
-void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
-                               unsigned int swap_en);
-void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
-                               unsigned int hs_zero);
-void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep);
-unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim);
-unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim,
-                                       unsigned int src);
-void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim,
-                                       unsigned int src, unsigned int enable);
-unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim);
-unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim);
-unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim);
-void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, unsigned int di,
-                               unsigned int data0, unsigned int data1);
-void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
-               unsigned int tx_data);
-void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim,
-               unsigned int data0, unsigned int data1);
-unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim);
-
-#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h
deleted file mode 100644 (file)
index 4227106..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/* linux/driver/video/exynos/exynos_mipi_dsi_regs.h
- *
- * Register definition file for Samsung MIPI-DSIM driver
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.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 _EXYNOS_MIPI_DSI_REGS_H
-#define _EXYNOS_MIPI_DSI_REGS_H
-
-#define EXYNOS_DSIM_STATUS             0x0     /* Status register */
-#define EXYNOS_DSIM_SWRST              0x4     /* Software reset register */
-#define EXYNOS_DSIM_CLKCTRL            0x8     /* Clock control register */
-#define EXYNOS_DSIM_TIMEOUT            0xc     /* Time out register */
-#define EXYNOS_DSIM_CONFIG             0x10    /* Configuration register */
-#define EXYNOS_DSIM_ESCMODE            0x14    /* Escape mode register */
-
-/* Main display image resolution register */
-#define EXYNOS_DSIM_MDRESOL            0x18
-#define EXYNOS_DSIM_MVPORCH            0x1c    /* Main display Vporch register */
-#define EXYNOS_DSIM_MHPORCH            0x20    /* Main display Hporch register */
-#define EXYNOS_DSIM_MSYNC              0x24    /* Main display sync area register */
-
-/* Sub display image resolution register */
-#define EXYNOS_DSIM_SDRESOL            0x28
-#define EXYNOS_DSIM_INTSRC             0x2c    /* Interrupt source register */
-#define EXYNOS_DSIM_INTMSK             0x30    /* Interrupt mask register */
-#define EXYNOS_DSIM_PKTHDR             0x34    /* Packet Header FIFO register */
-#define EXYNOS_DSIM_PAYLOAD            0x38    /* Payload FIFO register */
-#define EXYNOS_DSIM_RXFIFO             0x3c    /* Read FIFO register */
-#define EXYNOS_DSIM_FIFOTHLD           0x40    /* FIFO threshold level register */
-#define EXYNOS_DSIM_FIFOCTRL           0x44    /* FIFO status and control register */
-
-/* FIFO memory AC characteristic register */
-#define EXYNOS_DSIM_PLLCTRL            0x4c    /* PLL control register */
-#define EXYNOS_DSIM_PLLTMR             0x50    /* PLL timer register */
-#define EXYNOS_DSIM_PHYACCHR           0x54    /* D-PHY AC characteristic register */
-#define EXYNOS_DSIM_PHYACCHR1          0x58    /* D-PHY AC characteristic register1 */
-
-/* DSIM_STATUS */
-#define DSIM_STOP_STATE_DAT(x)         (((x) & 0xf) << 0)
-#define DSIM_STOP_STATE_CLK            (1 << 8)
-#define DSIM_TX_READY_HS_CLK           (1 << 10)
-
-/* DSIM_SWRST */
-#define DSIM_FUNCRST                   (1 << 16)
-#define DSIM_SWRST                     (1 << 0)
-
-/* EXYNOS_DSIM_TIMEOUT */
-#define DSIM_LPDR_TOUT_SHIFT(x)                ((x) << 0)
-#define DSIM_BTA_TOUT_SHIFT(x)         ((x) << 16)
-
-/* EXYNOS_DSIM_CLKCTRL */
-#define DSIM_LANE_ESC_CLKEN(x)         (((x) & 0x1f) << 19)
-#define DSIM_BYTE_CLKEN_SHIFT(x)       ((x) << 24)
-#define DSIM_BYTE_CLK_SRC_SHIFT(x)     ((x) << 25)
-#define DSIM_PLL_BYPASS_SHIFT(x)       ((x) << 27)
-#define DSIM_ESC_CLKEN_SHIFT(x)                ((x) << 28)
-#define DSIM_TX_REQUEST_HSCLK_SHIFT(x) ((x) << 31)
-
-/* EXYNOS_DSIM_CONFIG */
-#define DSIM_LANE_ENx(x)               (((x) & 0x1f) << 0)
-#define DSIM_NUM_OF_DATALANE_SHIFT(x)  ((x) << 5)
-#define DSIM_HSA_MODE_SHIFT(x)         ((x) << 20)
-#define DSIM_HBP_MODE_SHIFT(x)         ((x) << 21)
-#define DSIM_HFP_MODE_SHIFT(x)         ((x) << 22)
-#define DSIM_HSE_MODE_SHIFT(x)         ((x) << 23)
-#define DSIM_AUTO_MODE_SHIFT(x)                ((x) << 24)
-#define DSIM_EOT_DISABLE(x)            ((x) << 28)
-#define DSIM_AUTO_FLUSH(x)             ((x) << 29)
-
-#define DSIM_NUM_OF_DATA_LANE(x)       ((x) << DSIM_NUM_OF_DATALANE_SHIFT)
-
-/* EXYNOS_DSIM_ESCMODE */
-#define DSIM_TX_LPDT_LP                        (1 << 6)
-#define DSIM_CMD_LPDT_LP               (1 << 7)
-#define DSIM_FORCE_STOP_STATE_SHIFT(x) ((x) << 20)
-#define DSIM_STOP_STATE_CNT_SHIFT(x)   ((x) << 21)
-
-/* EXYNOS_DSIM_MDRESOL */
-#define DSIM_MAIN_STAND_BY             (1 << 31)
-#define DSIM_MAIN_VRESOL(x)            (((x) & 0x7ff) << 16)
-#define DSIM_MAIN_HRESOL(x)            (((x) & 0X7ff) << 0)
-
-/* EXYNOS_DSIM_MVPORCH */
-#define DSIM_CMD_ALLOW_SHIFT(x)                ((x) << 28)
-#define DSIM_STABLE_VFP_SHIFT(x)       ((x) << 16)
-#define DSIM_MAIN_VBP_SHIFT(x)         ((x) << 0)
-#define DSIM_CMD_ALLOW_MASK            (0xf << 28)
-#define DSIM_STABLE_VFP_MASK           (0x7ff << 16)
-#define DSIM_MAIN_VBP_MASK             (0x7ff << 0)
-
-/* EXYNOS_DSIM_MHPORCH */
-#define DSIM_MAIN_HFP_SHIFT(x)         ((x) << 16)
-#define DSIM_MAIN_HBP_SHIFT(x)         ((x) << 0)
-#define DSIM_MAIN_HFP_MASK             ((0xffff) << 16)
-#define DSIM_MAIN_HBP_MASK             ((0xffff) << 0)
-
-/* EXYNOS_DSIM_MSYNC */
-#define DSIM_MAIN_VSA_SHIFT(x)         ((x) << 22)
-#define DSIM_MAIN_HSA_SHIFT(x)         ((x) << 0)
-#define DSIM_MAIN_VSA_MASK             ((0x3ff) << 22)
-#define DSIM_MAIN_HSA_MASK             ((0xffff) << 0)
-
-/* EXYNOS_DSIM_SDRESOL */
-#define DSIM_SUB_STANDY_SHIFT(x)       ((x) << 31)
-#define DSIM_SUB_VRESOL_SHIFT(x)       ((x) << 16)
-#define DSIM_SUB_HRESOL_SHIFT(x)       ((x) << 0)
-#define DSIM_SUB_STANDY_MASK           ((0x1) << 31)
-#define DSIM_SUB_VRESOL_MASK           ((0x7ff) << 16)
-#define DSIM_SUB_HRESOL_MASK           ((0x7ff) << 0)
-
-/* EXYNOS_DSIM_INTSRC */
-#define INTSRC_PLL_STABLE              (1 << 31)
-#define INTSRC_SW_RST_RELEASE          (1 << 30)
-#define INTSRC_SFR_FIFO_EMPTY          (1 << 29)
-#define INTSRC_FRAME_DONE              (1 << 24)
-#define INTSRC_RX_DATA_DONE            (1 << 18)
-
-/* EXYNOS_DSIM_INTMSK */
-#define INTMSK_FIFO_EMPTY              (1 << 29)
-#define INTMSK_BTA                     (1 << 25)
-#define INTMSK_FRAME_DONE              (1 << 24)
-#define INTMSK_RX_TIMEOUT              (1 << 21)
-#define INTMSK_BTA_TIMEOUT             (1 << 20)
-#define INTMSK_RX_DONE                 (1 << 18)
-#define INTMSK_RX_TE                   (1 << 17)
-#define INTMSK_RX_ACK                  (1 << 16)
-#define INTMSK_RX_ECC_ERR              (1 << 15)
-#define INTMSK_RX_CRC_ERR              (1 << 14)
-
-/* EXYNOS_DSIM_FIFOCTRL */
-#define SFR_HEADER_EMPTY               (1 << 22)
-
-/* EXYNOS_DSIM_PHYACCHR */
-#define DSIM_AFC_CTL(x)                        (((x) & 0x7) << 5)
-
-/* EXYNOS_DSIM_PLLCTRL */
-#define DSIM_PLL_EN_SHIFT(x)           ((x) << 23)
-#define DSIM_FREQ_BAND_SHIFT(x)                ((x) << 24)
-
-#endif /* _EXYNOS_MIPI_DSI_REGS_H */
diff --git a/drivers/video/fbdev/exynos/s6e8ax0.c b/drivers/video/fbdev/exynos/s6e8ax0.c
deleted file mode 100644 (file)
index de2f3e7..0000000
+++ /dev/null
@@ -1,887 +0,0 @@
-/* linux/drivers/video/exynos/s6e8ax0.c
- *
- * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver.
- *
- * Inki Dae, <inki.dae@samsung.com>
- * Donghwa Lee, <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/ctype.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/lcd.h>
-#include <linux/fb.h>
-#include <linux/backlight.h>
-#include <linux/regulator/consumer.h>
-
-#include <video/mipi_display.h>
-#include <video/exynos_mipi_dsim.h>
-
-#define LDI_MTP_LENGTH         24
-#define DSIM_PM_STABLE_TIME    10
-#define MIN_BRIGHTNESS         0
-#define MAX_BRIGHTNESS         24
-#define GAMMA_TABLE_COUNT      26
-
-#define POWER_IS_ON(pwr)       ((pwr) == FB_BLANK_UNBLANK)
-#define POWER_IS_OFF(pwr)      ((pwr) == FB_BLANK_POWERDOWN)
-#define POWER_IS_NRM(pwr)      ((pwr) == FB_BLANK_NORMAL)
-
-#define lcd_to_master(a)       (a->dsim_dev->master)
-#define lcd_to_master_ops(a)   ((lcd_to_master(a))->master_ops)
-
-enum {
-       DSIM_NONE_STATE = 0,
-       DSIM_RESUME_COMPLETE = 1,
-       DSIM_FRAME_DONE = 2,
-};
-
-struct s6e8ax0 {
-       struct device   *dev;
-       unsigned int                    power;
-       unsigned int                    id;
-       unsigned int                    gamma;
-       unsigned int                    acl_enable;
-       unsigned int                    cur_acl;
-
-       struct lcd_device       *ld;
-       struct backlight_device *bd;
-
-       struct mipi_dsim_lcd_device     *dsim_dev;
-       struct lcd_platform_data        *ddi_pd;
-       struct mutex                    lock;
-       bool  enabled;
-};
-
-
-static struct regulator_bulk_data supplies[] = {
-       { .supply = "vdd3", },
-       { .supply = "vci", },
-};
-
-static void s6e8ax0_regulator_enable(struct s6e8ax0 *lcd)
-{
-       int ret = 0;
-       struct lcd_platform_data *pd = NULL;
-
-       pd = lcd->ddi_pd;
-       mutex_lock(&lcd->lock);
-       if (!lcd->enabled) {
-               ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
-               if (ret)
-                       goto out;
-
-               lcd->enabled = true;
-       }
-       msleep(pd->power_on_delay);
-out:
-       mutex_unlock(&lcd->lock);
-}
-
-static void s6e8ax0_regulator_disable(struct s6e8ax0 *lcd)
-{
-       int ret = 0;
-
-       mutex_lock(&lcd->lock);
-       if (lcd->enabled) {
-               ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
-               if (ret)
-                       goto out;
-
-               lcd->enabled = false;
-       }
-out:
-       mutex_unlock(&lcd->lock);
-}
-
-static const unsigned char s6e8ax0_22_gamma_30[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf,
-       0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0,
-       0x00, 0x61, 0x00, 0x5a, 0x00, 0x74,
-};
-
-static const unsigned char s6e8ax0_22_gamma_50[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0,
-       0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb,
-       0x00, 0x70, 0x00, 0x68, 0x00, 0x86,
-};
-
-static const unsigned char s6e8ax0_22_gamma_60[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4,
-       0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba,
-       0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d,
-};
-
-static const unsigned char s6e8ax0_22_gamma_70[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8,
-       0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9,
-       0x00, 0x7a, 0x00, 0x72, 0x00, 0x93,
-};
-
-static const unsigned char s6e8ax0_22_gamma_80[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9,
-       0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb,
-       0x00, 0x7f, 0x00, 0x77, 0x00, 0x99,
-};
-
-static const unsigned char s6e8ax0_22_gamma_90[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc,
-       0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9,
-       0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e,
-};
-
-static const unsigned char s6e8ax0_22_gamma_100[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce,
-       0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6,
-       0x00, 0x88, 0x00, 0x80, 0x00, 0xa5,
-};
-
-static const unsigned char s6e8ax0_22_gamma_120[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf,
-       0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6,
-       0x00, 0x8f, 0x00, 0x86, 0x00, 0xac,
-};
-
-static const unsigned char s6e8ax0_22_gamma_130[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0,
-       0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4,
-       0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1,
-};
-
-static const unsigned char s6e8ax0_22_gamma_140[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0,
-       0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4,
-       0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5,
-};
-
-static const unsigned char s6e8ax0_22_gamma_150[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0,
-       0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1,
-       0x00, 0x99, 0x00, 0x90, 0x00, 0xba,
-};
-
-static const unsigned char s6e8ax0_22_gamma_160[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0,
-       0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1,
-       0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe,
-};
-
-static const unsigned char s6e8ax0_22_gamma_170[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1,
-       0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1,
-       0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2,
-};
-
-static const unsigned char s6e8ax0_22_gamma_180[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2,
-       0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1,
-       0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5,
-};
-
-static const unsigned char s6e8ax0_22_gamma_190[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2,
-       0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf,
-       0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9,
-};
-
-static const unsigned char s6e8ax0_22_gamma_200[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2,
-       0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae,
-       0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd,
-};
-
-static const unsigned char s6e8ax0_22_gamma_210[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1,
-       0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad,
-       0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4,
-};
-
-static const unsigned char s6e8ax0_22_gamma_220[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1,
-       0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
-       0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3,
-};
-
-static const unsigned char s6e8ax0_22_gamma_230[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1,
-       0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
-       0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7,
-};
-
-static const unsigned char s6e8ax0_22_gamma_240[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2,
-       0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab,
-       0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb,
-};
-
-static const unsigned char s6e8ax0_22_gamma_250[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2,
-       0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab,
-       0x00, 0xb6, 0x00, 0xab, 0x00, 0xde,
-};
-
-static const unsigned char s6e8ax0_22_gamma_260[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1,
-       0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac,
-       0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0,
-};
-
-static const unsigned char s6e8ax0_22_gamma_270[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2,
-       0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa,
-       0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4,
-};
-
-static const unsigned char s6e8ax0_22_gamma_280[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0,
-       0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9,
-       0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7,
-};
-
-static const unsigned char s6e8ax0_22_gamma_300[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2,
-       0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9,
-       0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed,
-};
-
-static const unsigned char *s6e8ax0_22_gamma_table[] = {
-       s6e8ax0_22_gamma_30,
-       s6e8ax0_22_gamma_50,
-       s6e8ax0_22_gamma_60,
-       s6e8ax0_22_gamma_70,
-       s6e8ax0_22_gamma_80,
-       s6e8ax0_22_gamma_90,
-       s6e8ax0_22_gamma_100,
-       s6e8ax0_22_gamma_120,
-       s6e8ax0_22_gamma_130,
-       s6e8ax0_22_gamma_140,
-       s6e8ax0_22_gamma_150,
-       s6e8ax0_22_gamma_160,
-       s6e8ax0_22_gamma_170,
-       s6e8ax0_22_gamma_180,
-       s6e8ax0_22_gamma_190,
-       s6e8ax0_22_gamma_200,
-       s6e8ax0_22_gamma_210,
-       s6e8ax0_22_gamma_220,
-       s6e8ax0_22_gamma_230,
-       s6e8ax0_22_gamma_240,
-       s6e8ax0_22_gamma_250,
-       s6e8ax0_22_gamma_260,
-       s6e8ax0_22_gamma_270,
-       s6e8ax0_22_gamma_280,
-       s6e8ax0_22_gamma_300,
-};
-
-static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-
-       static const unsigned char data_to_send[] = {
-               0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
-               0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
-               0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
-               0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8
-       };
-       static const unsigned char data_to_send_panel_reverse[] = {
-               0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
-               0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
-               0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
-               0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1
-       };
-
-       if (lcd->dsim_dev->panel_reverse)
-               ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-                               data_to_send_panel_reverse,
-                               ARRAY_SIZE(data_to_send_panel_reverse));
-       else
-               ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-                               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xf2, 0x80, 0x03, 0x0d
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
-static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       unsigned int gamma = lcd->bd->props.brightness;
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-                       s6e8ax0_22_gamma_table[gamma],
-                       GAMMA_TABLE_COUNT);
-}
-
-static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xf7, 0x03
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send,
-               ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
-               0x0d, 0x00, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
-               0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xe3, 0x40
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE_PARAM,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xb1, 0x04, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
-               0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
-               0x64, 0xaf
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0x10, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0x11, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0x29, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0x28, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xf0, 0x5a, 0x5a
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_acl_on(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xc0, 0x01
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xc0, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-/* Full white 50% reducing setting */
-static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       /* Full white 50% reducing setting */
-       static const unsigned char cutoff_50[] = {
-               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
-               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
-               0x3f, 0x46
-       };
-       /* Full white 45% reducing setting */
-       static const unsigned char cutoff_45[] = {
-               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
-               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
-               0x37, 0x3d
-       };
-       /* Full white 40% reducing setting */
-       static const unsigned char cutoff_40[] = {
-               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
-               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
-               0x31, 0x36
-       };
-
-       if (lcd->acl_enable) {
-               if (lcd->cur_acl == 0) {
-                       if (lcd->gamma == 0 || lcd->gamma == 1) {
-                               s6e8ax0_acl_off(lcd);
-                               dev_dbg(&lcd->ld->dev,
-                                       "cur_acl=%d\n", lcd->cur_acl);
-                       } else
-                               s6e8ax0_acl_on(lcd);
-               }
-               switch (lcd->gamma) {
-               case 0: /* 30cd */
-                       s6e8ax0_acl_off(lcd);
-                       lcd->cur_acl = 0;
-                       break;
-               case 1 ... 3: /* 50cd ~ 90cd */
-                       ops->cmd_write(lcd_to_master(lcd),
-                               MIPI_DSI_DCS_LONG_WRITE,
-                               cutoff_40,
-                               ARRAY_SIZE(cutoff_40));
-                       lcd->cur_acl = 40;
-                       break;
-               case 4 ... 7: /* 120cd ~ 210cd */
-                       ops->cmd_write(lcd_to_master(lcd),
-                               MIPI_DSI_DCS_LONG_WRITE,
-                               cutoff_45,
-                               ARRAY_SIZE(cutoff_45));
-                       lcd->cur_acl = 45;
-                       break;
-               case 8 ... 10: /* 220cd ~ 300cd */
-                       ops->cmd_write(lcd_to_master(lcd),
-                               MIPI_DSI_DCS_LONG_WRITE,
-                               cutoff_50,
-                               ARRAY_SIZE(cutoff_50));
-                       lcd->cur_acl = 50;
-                       break;
-               default:
-                       break;
-               }
-       } else {
-               s6e8ax0_acl_off(lcd);
-               lcd->cur_acl = 0;
-               dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl);
-       }
-}
-
-static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
-{
-       unsigned int ret;
-       unsigned int addr = 0xd1;       /* MTP ID */
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-
-       ret = ops->cmd_read(lcd_to_master(lcd),
-                       MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM,
-                       addr, 3, mtp_id);
-}
-
-static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
-{
-       s6e8ax0_apply_level2_key(lcd);
-       s6e8ax0_sleep_out(lcd);
-       msleep(1);
-       s6e8ax0_panel_cond(lcd);
-       s6e8ax0_display_cond(lcd);
-       s6e8ax0_gamma_cond(lcd);
-       s6e8ax0_gamma_update(lcd);
-
-       s6e8ax0_etc_cond1(lcd);
-       s6e8ax0_etc_cond2(lcd);
-       s6e8ax0_etc_cond3(lcd);
-       s6e8ax0_etc_cond4(lcd);
-       s6e8ax0_etc_cond5(lcd);
-       s6e8ax0_etc_cond6(lcd);
-       s6e8ax0_etc_cond7(lcd);
-
-       s6e8ax0_elvss_nvm_set(lcd);
-       s6e8ax0_elvss_set(lcd);
-
-       s6e8ax0_acl_ctrl_set(lcd);
-       s6e8ax0_acl_on(lcd);
-
-       /* if ID3 value is not 33h, branch private elvss mode */
-       msleep(lcd->ddi_pd->power_on_delay);
-
-       return 0;
-}
-
-static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-                       s6e8ax0_22_gamma_table[brightness],
-                       ARRAY_SIZE(s6e8ax0_22_gamma_table));
-
-       /* update gamma table. */
-       s6e8ax0_gamma_update(lcd);
-       lcd->gamma = brightness;
-
-       return 0;
-}
-
-static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
-{
-       s6e8ax0_update_gamma_ctrl(lcd, gamma);
-
-       return 0;
-}
-
-static int s6e8ax0_set_power(struct lcd_device *ld, int power)
-{
-       struct s6e8ax0 *lcd = lcd_get_data(ld);
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       int ret = 0;
-
-       if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
-                       power != FB_BLANK_NORMAL) {
-               dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
-               return -EINVAL;
-       }
-
-       if ((power == FB_BLANK_UNBLANK) && ops->set_blank_mode) {
-               /* LCD power on */
-               if ((POWER_IS_ON(power) && POWER_IS_OFF(lcd->power))
-                       || (POWER_IS_ON(power) && POWER_IS_NRM(lcd->power))) {
-                       ret = ops->set_blank_mode(lcd_to_master(lcd), power);
-                       if (!ret && lcd->power != power)
-                               lcd->power = power;
-               }
-       } else if ((power == FB_BLANK_POWERDOWN) && ops->set_early_blank_mode) {
-               /* LCD power off */
-               if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) ||
-               (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) {
-                       ret = ops->set_early_blank_mode(lcd_to_master(lcd),
-                                                       power);
-                       if (!ret && lcd->power != power)
-                               lcd->power = power;
-               }
-       }
-
-       return ret;
-}
-
-static int s6e8ax0_get_power(struct lcd_device *ld)
-{
-       struct s6e8ax0 *lcd = lcd_get_data(ld);
-
-       return lcd->power;
-}
-
-static int s6e8ax0_set_brightness(struct backlight_device *bd)
-{
-       int ret = 0, brightness = bd->props.brightness;
-       struct s6e8ax0 *lcd = bl_get_data(bd);
-
-       if (brightness < MIN_BRIGHTNESS ||
-               brightness > bd->props.max_brightness) {
-               dev_err(lcd->dev, "lcd brightness should be %d to %d.\n",
-                       MIN_BRIGHTNESS, MAX_BRIGHTNESS);
-               return -EINVAL;
-       }
-
-       ret = s6e8ax0_gamma_ctrl(lcd, brightness);
-       if (ret) {
-               dev_err(&bd->dev, "lcd brightness setting failed.\n");
-               return -EIO;
-       }
-
-       return ret;
-}
-
-static struct lcd_ops s6e8ax0_lcd_ops = {
-       .set_power = s6e8ax0_set_power,
-       .get_power = s6e8ax0_get_power,
-};
-
-static const struct backlight_ops s6e8ax0_backlight_ops = {
-       .update_status = s6e8ax0_set_brightness,
-};
-
-static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power)
-{
-       struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
-
-       msleep(lcd->ddi_pd->power_on_delay);
-
-       /* lcd power on */
-       if (power)
-               s6e8ax0_regulator_enable(lcd);
-       else
-               s6e8ax0_regulator_disable(lcd);
-
-       msleep(lcd->ddi_pd->reset_delay);
-
-       /* lcd reset */
-       if (lcd->ddi_pd->reset)
-               lcd->ddi_pd->reset(lcd->ld);
-       msleep(5);
-}
-
-static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev)
-{
-       struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
-
-       s6e8ax0_panel_init(lcd);
-       s6e8ax0_display_on(lcd);
-
-       lcd->power = FB_BLANK_UNBLANK;
-}
-
-static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
-{
-       struct s6e8ax0 *lcd;
-       int ret;
-       u8 mtp_id[3] = {0, };
-
-       lcd = devm_kzalloc(&dsim_dev->dev, sizeof(struct s6e8ax0), GFP_KERNEL);
-       if (!lcd) {
-               dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n");
-               return -ENOMEM;
-       }
-
-       lcd->dsim_dev = dsim_dev;
-       lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data;
-       lcd->dev = &dsim_dev->dev;
-
-       mutex_init(&lcd->lock);
-
-       ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
-       if (ret) {
-               dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
-               return ret;
-       }
-
-       lcd->ld = devm_lcd_device_register(lcd->dev, "s6e8ax0", lcd->dev, lcd,
-                       &s6e8ax0_lcd_ops);
-       if (IS_ERR(lcd->ld)) {
-               dev_err(lcd->dev, "failed to register lcd ops.\n");
-               return PTR_ERR(lcd->ld);
-       }
-
-       lcd->bd = devm_backlight_device_register(lcd->dev, "s6e8ax0-bl",
-                               lcd->dev, lcd, &s6e8ax0_backlight_ops, NULL);
-       if (IS_ERR(lcd->bd)) {
-               dev_err(lcd->dev, "failed to register backlight ops.\n");
-               return PTR_ERR(lcd->bd);
-       }
-
-       lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
-       lcd->bd->props.brightness = MAX_BRIGHTNESS;
-
-       s6e8ax0_read_id(lcd, mtp_id);
-       if (mtp_id[0] == 0x00)
-               dev_err(lcd->dev, "read id failed\n");
-
-       dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
-                       mtp_id[0], mtp_id[1], mtp_id[2]);
-
-       if (mtp_id[2] == 0x33)
-               dev_info(lcd->dev,
-                       "ID-3 is 0xff does not support dynamic elvss\n");
-       else
-               dev_info(lcd->dev,
-                       "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
-
-       lcd->acl_enable = 1;
-       lcd->cur_acl = 0;
-
-       dev_set_drvdata(&dsim_dev->dev, lcd);
-
-       dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n");
-
-       return 0;
-}
-
-static int __maybe_unused s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev)
-{
-       struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
-
-       s6e8ax0_sleep_in(lcd);
-       msleep(lcd->ddi_pd->power_off_delay);
-       s6e8ax0_display_off(lcd);
-
-       s6e8ax0_regulator_disable(lcd);
-
-       return 0;
-}
-
-static int __maybe_unused s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev)
-{
-       struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
-
-       s6e8ax0_sleep_out(lcd);
-       msleep(lcd->ddi_pd->power_on_delay);
-
-       s6e8ax0_regulator_enable(lcd);
-       s6e8ax0_set_sequence(dsim_dev);
-
-       return 0;
-}
-
-static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = {
-       .name = "s6e8ax0",
-       .id = -1,
-
-       .power_on = s6e8ax0_power_on,
-       .set_sequence = s6e8ax0_set_sequence,
-       .probe = s6e8ax0_probe,
-       .suspend = IS_ENABLED(CONFIG_PM) ? s6e8ax0_suspend : NULL,
-       .resume = IS_ENABLED(CONFIG_PM) ? s6e8ax0_resume : NULL,
-};
-
-static int s6e8ax0_init(void)
-{
-       exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver);
-
-       return 0;
-}
-
-static void s6e8ax0_exit(void)
-{
-       return;
-}
-
-module_init(s6e8ax0_init);
-module_exit(s6e8ax0_exit);
-
-MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
-MODULE_LICENSE("GPL");
index e4031ef394917fc1a2881c7e40a9030ebfb15620..8577195cb533050d4bfe0f6d8ec8c09c984bebec 100644 (file)
@@ -47,7 +47,7 @@
 #define DPY_W 600
 #define DPY_H 800
 
-static struct fb_fix_screeninfo hecubafb_fix = {
+static const struct fb_fix_screeninfo hecubafb_fix = {
        .id =           "hecubafb",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_MONO01,
@@ -58,7 +58,7 @@ static struct fb_fix_screeninfo hecubafb_fix = {
        .accel =        FB_ACCEL_NONE,
 };
 
-static struct fb_var_screeninfo hecubafb_var = {
+static const struct fb_var_screeninfo hecubafb_var = {
        .xres           = DPY_W,
        .yres           = DPY_H,
        .xres_virtual   = DPY_W,
index 15d3ccff296542b74c6ccab42f5c1c1fac1c986f..46302854317367e7649a7929cde2f10a2e882dc9 100644 (file)
@@ -106,7 +106,7 @@ static DEFINE_SPINLOCK(hga_reg_lock);
 
 /* Framebuffer driver structures */
 
-static struct fb_var_screeninfo hga_default_var = {
+static const struct fb_var_screeninfo hga_default_var = {
        .xres           = 720,
        .yres           = 348,
        .xres_virtual   = 720,
index cf5ccd0f22521818c952fe2048907ca6bfe37b1a..7bc5f6056c77df7cd2293ffd5f4514080a6019b7 100644 (file)
@@ -82,7 +82,7 @@ struct i740fb_par {
 #define DACSPEED24_SD  128
 #define DACSPEED32     86
 
-static struct fb_fix_screeninfo i740fb_fix = {
+static const struct fb_fix_screeninfo i740fb_fix = {
        .id =           "i740fb",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_TRUECOLOR,
index 025b882a4826dee272abc930897c6fd98fd09e71..483ab2592d0c056e9920834303b722105e5eca16 100644 (file)
@@ -1691,7 +1691,7 @@ static int i810_alloc_agp_mem(struct fb_info *info)
        if (!(par->i810_gtt.i810_cursor_memory = 
              agp_allocate_memory(bridge, par->cursor_heap.size >> 12,
                                  AGP_PHYSICAL_MEMORY))) {
-               printk("i810fb_alloc_cursormem:  can't allocate
+               printk("i810fb_alloc_cursormem:  can't allocate "
                       "cursor memory\n");
                agp_backend_release(bridge);
                return -ENOMEM;
index bf207444ba0c64a2fd27fcd80959c0e5f1706ed4..ff2a5d2023e1f88078c90b56746303783cbd7429 100644 (file)
@@ -1301,11 +1301,6 @@ static int intelfb_check_var(struct fb_var_screeninfo *var,
                break;
        }
 
-       if (v.xoffset < 0)
-               v.xoffset = 0;
-       if (v.yoffset < 0)
-               v.yoffset = 0;
-
        if (v.xoffset > v.xres_virtual - v.xres)
                v.xoffset = v.xres_virtual - v.xres;
        if (v.yoffset > v.yres_virtual - v.yres)
index 5bb01533271e176122d0fb5a43a0b6875757d4cb..f77478fb3d14a3dcc8d013824994a378ba541b4d 100644 (file)
@@ -44,7 +44,7 @@ static struct fb_fix_screeninfo kyro_fix = {
        .accel          = FB_ACCEL_NONE,
 };
 
-static struct fb_var_screeninfo kyro_var = {
+static const struct fb_var_screeninfo kyro_var = {
        /* 640x480, 16bpp @ 60 Hz */
        .xres           = 640,
        .yres           = 480,
index 195ad7cac1baccd70a0b681832c85bd120209d91..68fa037d8cbc06a36c257f828264180efce6fd24 100644 (file)
@@ -372,7 +372,7 @@ static int Ti3026_init(struct matrox_fb_info *minfo, struct my_timming *m)
 
        DBG(__func__)
 
-       memcpy(hw->DACreg, MGADACbpp32, sizeof(hw->DACreg));
+       memcpy(hw->DACreg, MGADACbpp32, sizeof(MGADACbpp32));
        switch (minfo->fbcon.var.bits_per_pixel) {
                case 4: hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_16_1;       /* or _8_1, they are same */
                        hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR;
index cff0546ea6fd40ef37cb1f45f435b35b522b7999..f108ae66fc83f0a2b7c8d5bd79faae49b82a2ec2 100644 (file)
@@ -433,7 +433,7 @@ static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct outp
                0x00,   /* 3E written multiple times */
                0x00,   /* 3F not written */
        } };
-       static struct mavenregs ntscregs = { {
+       static const struct mavenregs ntscregs = { {
                0x21, 0xF0, 0x7C, 0x1F, /* 00: chroma subcarrier */
                0x00,
                0x00,   /* test */
index c87e17afb3e2c9c74d9131c578235672d325f3d8..ba96c44f27612973b8f0bcea357d4b4e2ca0d200 100644 (file)
@@ -157,17 +157,10 @@ static struct i2c_adapter mb862xx_i2c_adapter = {
 
 int mb862xx_i2c_init(struct mb862xxfb_par *par)
 {
-       int ret;
-
        mb862xx_i2c_adapter.algo_data = par;
        par->adap = &mb862xx_i2c_adapter;
 
-       ret = i2c_add_adapter(par->adap);
-       if (ret < 0) {
-               dev_err(par->dev, "failed to add %s\n",
-                       mb862xx_i2c_adapter.name);
-       }
-       return ret;
+       return i2c_add_adapter(par->adap);
 }
 
 void mb862xx_i2c_exit(struct mb862xxfb_par *par)
index f91b1db262b04a2b131911009e3c69914b254013..8778e01cebac642fe4a03a7d292c967d9e3eb13a 100644 (file)
@@ -845,7 +845,7 @@ static int __set_par(struct fb_info *fbi, bool lock)
                if (fbi->var.sync & FB_SYNC_SHARP_MODE)
                        mode = IPU_PANEL_SHARP_TFT;
 
-               dev_dbg(fbi->device, "pixclock = %ul Hz\n",
+               dev_dbg(fbi->device, "pixclock = %u Hz\n",
                        (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
 
                if (sdc_init_panel(mx3fb, mode,
index 4e6608ceac09c20f8d85d844386d22c1207e8230..7846f0e8bbbb55f9ad691b733b6d2333452a672e 100644 (file)
@@ -800,6 +800,7 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host,
                        struct fb_videomode *vmode)
 {
        int ret;
+       struct device *dev = &host->pdev->dev;
        struct fb_info *fb_info = &host->fb_info;
        struct fb_var_screeninfo *var = &fb_info->var;
        dma_addr_t fb_phys;
@@ -825,12 +826,10 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host,
 
        /* Memory allocation for framebuffer */
        fb_size = SZ_2M;
-       fb_virt = alloc_pages_exact(fb_size, GFP_DMA);
+       fb_virt = dma_alloc_wc(dev, PAGE_ALIGN(fb_size), &fb_phys, GFP_KERNEL);
        if (!fb_virt)
                return -ENOMEM;
 
-       fb_phys = virt_to_phys(fb_virt);
-
        fb_info->fix.smem_start = fb_phys;
        fb_info->screen_base = fb_virt;
        fb_info->screen_size = fb_info->fix.smem_len = fb_size;
@@ -843,9 +842,11 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host,
 
 static void mxsfb_free_videomem(struct mxsfb_info *host)
 {
+       struct device *dev = &host->pdev->dev;
        struct fb_info *fb_info = &host->fb_info;
 
-       free_pages_exact(fb_info->screen_base, fb_info->fix.smem_len);
+       dma_free_wc(dev, fb_info->screen_size, fb_info->screen_base,
+                   fb_info->fix.smem_start);
 }
 
 static const struct platform_device_id mxsfb_devtype[] = {
index fb60a8f0cc94c8103d8dc141b25c5eecec5db39c..906c6e75c260180705d5bbdd1f6228cd0e0d315d 100644 (file)
@@ -625,6 +625,21 @@ static void __init offb_init_nodriver(struct device_node *dp, int no_real_node)
        if (address == OF_BAD_ADDR && addr_prop)
                address = (u64)addr_prop;
        if (address != OF_BAD_ADDR) {
+#ifdef CONFIG_PCI
+               const __be32 *vidp, *didp;
+               u32 vid, did;
+               struct pci_dev *pdev;
+
+               vidp = of_get_property(dp, "vendor-id", NULL);
+               didp = of_get_property(dp, "device-id", NULL);
+               if (vidp && didp) {
+                       vid = be32_to_cpup(vidp);
+                       did = be32_to_cpup(didp);
+                       pdev = pci_get_device(vid, did, NULL);
+                       if (!pdev || pci_enable_device(pdev))
+                               return;
+               }
+#endif
                /* kludge for valkyrie */
                if (strcmp(dp->name, "valkyrie") == 0)
                        address += 0x1000;
index 0e4cee9a8d796b70ae5eefb585cff35bba292a01..c81f150589e1f8a2c4a9c342d305ef2684f121cb 100644 (file)
@@ -60,7 +60,6 @@ struct mipid_device {
        struct mutex            mutex;
        struct lcd_panel        panel;
 
-       struct workqueue_struct *esd_wq;
        struct delayed_work     esd_work;
        void                    (*esd_check)(struct mipid_device *m);
 };
@@ -390,7 +389,7 @@ static void ls041y3_esd_check(struct mipid_device *md)
 static void mipid_esd_start_check(struct mipid_device *md)
 {
        if (md->esd_check != NULL)
-               queue_delayed_work(md->esd_wq, &md->esd_work,
+               schedule_delayed_work(&md->esd_work,
                                   MIPID_ESD_CHECK_PERIOD);
 }
 
@@ -476,11 +475,6 @@ static int mipid_init(struct lcd_panel *panel,
        struct mipid_device *md = to_mipid_device(panel);
 
        md->fbdev = fbdev;
-       md->esd_wq = create_singlethread_workqueue("mipid_esd");
-       if (md->esd_wq == NULL) {
-               dev_err(&md->spi->dev, "can't create ESD workqueue\n");
-               return -ENOMEM;
-       }
        INIT_DELAYED_WORK(&md->esd_work, mipid_esd_work);
        mutex_init(&md->mutex);
 
@@ -500,7 +494,6 @@ static void mipid_cleanup(struct lcd_panel *panel)
 
        if (md->enabled)
                mipid_esd_stop_check(md);
-       destroy_workqueue(md->esd_wq);
 }
 
 static struct lcd_panel mipid_panel = {
index b58012b82b6fe93fb81b2429fb2d060c43c9bf9e..8b810696a42b6c421db61cadec9774d590959f48 100644 (file)
@@ -75,8 +75,6 @@ struct panel_drv_data {
 
        bool intro_printed;
 
-       struct workqueue_struct *workqueue;
-
        bool ulps_enabled;
        unsigned ulps_timeout;
        struct delayed_work ulps_work;
@@ -232,7 +230,7 @@ static int dsicm_set_update_window(struct panel_drv_data *ddata,
 static void dsicm_queue_ulps_work(struct panel_drv_data *ddata)
 {
        if (ddata->ulps_timeout > 0)
-               queue_delayed_work(ddata->workqueue, &ddata->ulps_work,
+               schedule_delayed_work(&ddata->ulps_work,
                                msecs_to_jiffies(ddata->ulps_timeout));
 }
 
@@ -1244,11 +1242,6 @@ static int dsicm_probe(struct platform_device *pdev)
                dev_dbg(dev, "Using GPIO TE\n");
        }
 
-       ddata->workqueue = create_singlethread_workqueue("dsicm_wq");
-       if (ddata->workqueue == NULL) {
-               dev_err(dev, "can't create workqueue\n");
-               return -ENOMEM;
-       }
        INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);
 
        dsicm_hw_reset(ddata);
@@ -1262,7 +1255,7 @@ static int dsicm_probe(struct platform_device *pdev)
                                dev, ddata, &dsicm_bl_ops, &props);
                if (IS_ERR(bldev)) {
                        r = PTR_ERR(bldev);
-                       goto err_bl;
+                       goto err_reg;
                }
 
                ddata->bldev = bldev;
@@ -1285,8 +1278,6 @@ static int dsicm_probe(struct platform_device *pdev)
 err_sysfs_create:
        if (bldev != NULL)
                backlight_device_unregister(bldev);
-err_bl:
-       destroy_workqueue(ddata->workqueue);
 err_reg:
        return r;
 }
@@ -1316,7 +1307,6 @@ static int __exit dsicm_remove(struct platform_device *pdev)
        omap_dss_put_device(ddata->in);
 
        dsicm_cancel_ulps_work(ddata);
-       destroy_workqueue(ddata->workqueue);
 
        /* reset, to be sure that the panel is in a valid state */
        dsicm_hw_reset(ddata);
index 3691bde4ce0a01a0d7ae76aaec3e3779db4d54c7..a864608c5df1242a1a84cdca86b34c9cbce2034f 100644 (file)
@@ -644,6 +644,7 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
 {
 
        int r;
+       long time_left;
        DECLARE_COMPLETION_ONSTACK(completion);
 
        r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
@@ -652,15 +653,15 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
        if (r)
                return r;
 
-       timeout = wait_for_completion_interruptible_timeout(&completion,
+       time_left = wait_for_completion_interruptible_timeout(&completion,
                        timeout);
 
        omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
 
-       if (timeout == 0)
+       if (time_left == 0)
                return -ETIMEDOUT;
 
-       if (timeout == -ERESTARTSYS)
+       if (time_left == -ERESTARTSYS)
                return -ERESTARTSYS;
 
        return 0;
index 9e4800a4e3d11cff2ec29f78a260d4777c19784e..30d49f3800b334b0a3f6b8ed29ea437d30cb632e 100644 (file)
@@ -1167,7 +1167,6 @@ static int dsi_regulator_init(struct platform_device *dsidev)
 {
        struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
        struct regulator *vdds_dsi;
-       int r;
 
        if (dsi->vdds_dsi_reg != NULL)
                return 0;
@@ -1180,13 +1179,6 @@ static int dsi_regulator_init(struct platform_device *dsidev)
                return PTR_ERR(vdds_dsi);
        }
 
-       r = regulator_set_voltage(vdds_dsi, 1800000, 1800000);
-       if (r) {
-               devm_regulator_put(vdds_dsi);
-               DSSERR("can't set the DSI regulator voltage\n");
-               return r;
-       }
-
        dsi->vdds_dsi_reg = vdds_dsi;
 
        return 0;
@@ -5348,7 +5340,7 @@ static int dsi_bind(struct device *dev, struct device *master, void *data)
 
        dsi->phy_base = devm_ioremap(&dsidev->dev, res->start,
                resource_size(res));
-       if (!dsi->proto_base) {
+       if (!dsi->phy_base) {
                DSSERR("can't ioremap DSI PHY\n");
                return -ENOMEM;
        }
@@ -5368,7 +5360,7 @@ static int dsi_bind(struct device *dev, struct device *master, void *data)
 
        dsi->pll_base = devm_ioremap(&dsidev->dev, res->start,
                resource_size(res));
-       if (!dsi->proto_base) {
+       if (!dsi->pll_base) {
                DSSERR("can't ioremap DSI PLL\n");
                return -ENOMEM;
        }
index 926a6f20dbb233ea541dc2dbbd1e90c0484e3a1a..156a254705ea5e090bbd1e3c1915adb96264e238 100644 (file)
@@ -100,7 +100,6 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data)
 
 static int hdmi_init_regulator(void)
 {
-       int r;
        struct regulator *reg;
 
        if (hdmi.vdda_reg != NULL)
@@ -114,13 +113,6 @@ static int hdmi_init_regulator(void)
                return PTR_ERR(reg);
        }
 
-       r = regulator_set_voltage(reg, 1800000, 1800000);
-       if (r) {
-               devm_regulator_put(reg);
-               DSSWARN("can't set the regulator voltage\n");
-               return r;
-       }
-
        hdmi.vdda_reg = reg;
 
        return 0;
index 0ee829a165c3a974f6fb6560543303a97cd761da..4da36bcab97798c370971731c0541772b357378c 100644 (file)
@@ -119,7 +119,6 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data)
 
 static int hdmi_init_regulator(void)
 {
-       int r;
        struct regulator *reg;
 
        if (hdmi.vdda_reg != NULL)
@@ -131,13 +130,6 @@ static int hdmi_init_regulator(void)
                return PTR_ERR(reg);
        }
 
-       r = regulator_set_voltage(reg, 1800000, 1800000);
-       if (r) {
-               devm_regulator_put(reg);
-               DSSWARN("can't set the regulator voltage\n");
-               return r;
-       }
-
        hdmi.vdda_reg = reg;
 
        return 0;
index aa8d28880912dc571cdfe7763a8edd2223a28065..1a4070f719c298f18d3b39cddb6c0cdd8f3a135a 100644 (file)
@@ -113,7 +113,7 @@ static struct fb_fix_screeninfo pm2fb_fix = {
 /*
  * Default video mode. In case the modedb doesn't work.
  */
-static struct fb_var_screeninfo pm2fb_var = {
+static const struct fb_var_screeninfo pm2fb_var = {
        /* "640x480, 8 bpp @ 60 Hz */
        .xres =                 640,
        .yres =                 480,
index 3b1ca441107370d39c54bd4bffa24248eaa730c5..a2564ab91e62d3c2775441d292e54e1d25492a40 100644 (file)
@@ -686,8 +686,8 @@ static ssize_t pvr2fb_write(struct fb_info *info, const char *buf,
        if (!pages)
                return -ENOMEM;
 
-       ret = get_user_pages_unlocked((unsigned long)buf, nr_pages, WRITE,
-                       0, pages);
+       ret = get_user_pages_unlocked((unsigned long)buf, nr_pages, pages,
+                       FOLL_WRITE);
 
        if (ret < nr_pages) {
                nr_pages = ret;
index 2c0487f4f805cf7d4c9153d4c966f7cc67c45c81..ef73f14d7ba00d4b185739d65006cb50ebea7580 100644 (file)
@@ -2125,7 +2125,7 @@ static int of_get_pxafb_display(struct device *dev, struct device_node *disp,
 
        timings = of_get_display_timings(disp);
        if (!timings)
-               goto out;
+               return -EINVAL;
 
        ret = -ENOMEM;
        info->modes = kmalloc_array(timings->num_timings,
@@ -2186,6 +2186,7 @@ static int of_get_pxafb_mode_info(struct device *dev,
        ret = of_property_read_u32(np, "bus-width", &bus_width);
        if (ret) {
                dev_err(dev, "no bus-width specified: %d\n", ret);
+               of_node_put(np);
                return ret;
        }
 
index 96aa46dc696c94225be2b9a721e6cba6e619af46..5d6179ef02980e7cbffd9be5e4b8987c0c89d0cb 100644 (file)
@@ -83,7 +83,7 @@ static const char *s1d13xxxfb_prod_names[] = {
 /*
  * here we define the default struct fb_fix_screeninfo
  */
-static struct fb_fix_screeninfo s1d13xxxfb_fix = {
+static const struct fb_fix_screeninfo s1d13xxxfb_fix = {
        .id             = S1D_FBID,
        .type           = FB_TYPE_PACKED_PIXELS,
        .visual         = FB_VISUAL_PSEUDOCOLOR,
@@ -929,7 +929,7 @@ static int s1d13xxxfb_suspend(struct platform_device *dev, pm_message_t state)
                s1dfb->disp_save = kmalloc(info->fix.smem_len, GFP_KERNEL);
 
        if (!s1dfb->disp_save) {
-               printk(KERN_ERR PFX "no memory to save screen");
+               printk(KERN_ERR PFX "no memory to save screen\n");
                return -ENOMEM;
        }
 
index 0dd86be36afbb5bdff4d268ae58c17337a98c6b9..a67e4567e65619632fc8479b8feb9133bf801239 100644 (file)
@@ -767,7 +767,7 @@ static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
 
 static int s3c2410fb_cpufreq_transition(struct notifier_block *nb,
                                        unsigned long val, void *data)
index 47a17bd2301169d9368d041b83d2b2b9d80835bf..cdd11e2f885910def4f7d8537dbf4943aedaa568 100644 (file)
@@ -32,7 +32,7 @@ struct s3c2410fb_info {
        unsigned long           clk_rate;
        unsigned int            palette_ready;
 
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
        struct notifier_block   freq_transition;
 #endif
 
index 6c77ab09b0b202db95718c67cc2ecbd415f8f1bc..c30a91c1137c6970821317e643f4f90c38878b4f 100644 (file)
@@ -1660,7 +1660,7 @@ static struct fb_ops savagefb_ops = {
 
 /* --------------------------------------------------------------------- */
 
-static struct fb_var_screeninfo savagefb_var800x600x8 = {
+static const struct fb_var_screeninfo savagefb_var800x600x8 = {
        .accel_flags =  FB_ACCELF_TEXT,
        .xres =         800,
        .yres =         600,
index e9cf1997728516b8cab6a1cfd67925151ee3e7c9..61f799a515dc797e36aaa3f203f76153f59d802e 100644 (file)
 #include <linux/parser.h>
 #include <linux/regulator/consumer.h>
 
-static struct fb_fix_screeninfo simplefb_fix = {
+static const struct fb_fix_screeninfo simplefb_fix = {
        .id             = "simple",
        .type           = FB_TYPE_PACKED_PIXELS,
        .visual         = FB_VISUAL_TRUECOLOR,
        .accel          = FB_ACCEL_NONE,
 };
 
-static struct fb_var_screeninfo simplefb_var = {
+static const struct fb_var_screeninfo simplefb_var = {
        .height         = -1,
        .width          = -1,
        .activate       = FB_ACTIVATE_NOW,
@@ -74,8 +74,14 @@ static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
        return 0;
 }
 
+struct simplefb_par;
+static void simplefb_clocks_destroy(struct simplefb_par *par);
+static void simplefb_regulators_destroy(struct simplefb_par *par);
+
 static void simplefb_destroy(struct fb_info *info)
 {
+       simplefb_regulators_destroy(info->par);
+       simplefb_clocks_destroy(info->par);
        if (info->screen_base)
                iounmap(info->screen_base);
 }
@@ -487,11 +493,8 @@ error_fb_release:
 static int simplefb_remove(struct platform_device *pdev)
 {
        struct fb_info *info = platform_get_drvdata(pdev);
-       struct simplefb_par *par = info->par;
 
        unregister_framebuffer(info);
-       simplefb_regulators_destroy(par);
-       simplefb_clocks_destroy(par);
        framebuffer_release(info);
 
        return 0;
index 86ae1d4556fc1e139524d5c748933dd2d9b7718f..73cb4ffff3c5910f5dfb8b41f5e40f2849803459 100644 (file)
@@ -56,7 +56,7 @@ struct smtcfb_info {
 
 void __iomem *smtc_regbaseaddress;     /* Memory Map IO starting address */
 
-static struct fb_var_screeninfo smtcfb_var = {
+static const struct fb_var_screeninfo smtcfb_var = {
        .xres           = 1024,
        .yres           = 600,
        .xres_virtual   = 1024,
index 9279e5f6696e24ad84ea11a5e3adf7c1980c9a91..ec2e7e3536859cae3294d484c5bb56b5330c6db5 100644 (file)
@@ -1761,10 +1761,8 @@ error:
 static void ufx_usb_disconnect(struct usb_interface *interface)
 {
        struct ufx_data *dev;
-       struct fb_info *info;
 
        dev = usb_get_intfdata(interface);
-       info = dev->info;
 
        pr_debug("USB disconnect starting\n");
 
index a9c45c89b15eaca20f6a62957ff92dd20775a6de..2925d5ce8d3e561c1d674cab95be1ce757ed8f39 100644 (file)
@@ -64,7 +64,7 @@ struct ssd1307fb_par {
        u32 contrast;
        u32 dclk_div;
        u32 dclk_frq;
-       struct ssd1307fb_deviceinfo *device_info;
+       const struct ssd1307fb_deviceinfo *device_info;
        struct i2c_client *client;
        u32 height;
        struct fb_info *info;
@@ -84,7 +84,7 @@ struct ssd1307fb_array {
        u8      data[0];
 };
 
-static struct fb_fix_screeninfo ssd1307fb_fix = {
+static const struct fb_fix_screeninfo ssd1307fb_fix = {
        .id             = "Solomon SSD1307",
        .type           = FB_TYPE_PACKED_PIXELS,
        .visual         = FB_VISUAL_MONO10,
@@ -94,7 +94,7 @@ static struct fb_fix_screeninfo ssd1307fb_fix = {
        .accel          = FB_ACCEL_NONE,
 };
 
-static struct fb_var_screeninfo ssd1307fb_var = {
+static const struct fb_var_screeninfo ssd1307fb_var = {
        .bits_per_pixel = 1,
 };
 
@@ -559,8 +559,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
        par->info = info;
        par->client = client;
 
-       par->device_info = (struct ssd1307fb_deviceinfo *)of_match_device(
-                       ssd1307fb_of_match, &client->dev)->data;
+       par->device_info = of_device_get_match_data(&client->dev);
 
        par->reset = of_get_named_gpio(client->dev.of_node,
                                         "reset-gpios", 0);
index 621fa441a6db225755145cd859fd51d48d62482f..d5fa313806fe7ebd63fb6f2889bd5306c0003432 100644 (file)
@@ -82,7 +82,7 @@
 #define VOODOO3_MAX_PIXCLOCK 300000
 #define VOODOO5_MAX_PIXCLOCK 350000
 
-static struct fb_fix_screeninfo tdfx_fix = {
+static const struct fb_fix_screeninfo tdfx_fix = {
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_PSEUDOCOLOR,
        .ypanstep =     1,
@@ -90,7 +90,7 @@ static struct fb_fix_screeninfo tdfx_fix = {
        .accel =        FB_ACCEL_3DFX_BANSHEE
 };
 
-static struct fb_var_screeninfo tdfx_var = {
+static const struct fb_var_screeninfo tdfx_var = {
        /* "640x480, 8 bpp @ 60 Hz */
        .xres =         640,
        .yres =         480,
index 178ae93b7ebd6a89e946803832dd3ba032b0f9c8..98af9e02959b43d87758903dfd72a81641261785 100644 (file)
@@ -33,7 +33,7 @@ static struct cb_id uvesafb_cn_id = {
 static char v86d_path[PATH_MAX] = "/sbin/v86d";
 static char v86d_started;      /* has v86d been started by uvesafb? */
 
-static struct fb_fix_screeninfo uvesafb_fix = {
+static const struct fb_fix_screeninfo uvesafb_fix = {
        .id     = "VESA VGA",
        .type   = FB_TYPE_PACKED_PIXELS,
        .accel  = FB_ACCEL_NONE,
index b9c2f81fb6b9f047bfcd2e144140cce80c3731c0..da653a080394c9a71ef16d414f2b3ccd5fa6a037 100644 (file)
 static void *videomemory;
 static u_long videomemorysize = VIDEOMEMSIZE;
 module_param(videomemorysize, ulong, 0);
+MODULE_PARM_DESC(videomemorysize, "RAM available to frame buffer (in bytes)");
 
-/**********************************************************************
- *
- * Memory management
- *
- **********************************************************************/
-static void *rvmalloc(unsigned long size)
-{
-       void *mem;
-       unsigned long adr;
-
-       size = PAGE_ALIGN(size);
-       mem = vmalloc_32(size);
-       if (!mem)
-               return NULL;
-
-       /*
-        * VFB must clear memory to prevent kernel info
-        * leakage into userspace
-        * VGA-based drivers MUST NOT clear memory if
-        * they want to be able to take over vgacon
-        */
-
-       memset(mem, 0, size);
-       adr = (unsigned long) mem;
-       while (size > 0) {
-               SetPageReserved(vmalloc_to_page((void *)adr));
-               adr += PAGE_SIZE;
-               size -= PAGE_SIZE;
-       }
-
-       return mem;
-}
-
-static void rvfree(void *mem, unsigned long size)
-{
-       unsigned long adr;
-
-       if (!mem)
-               return;
-
-       adr = (unsigned long) mem;
-       while ((long) size > 0) {
-               ClearPageReserved(vmalloc_to_page((void *)adr));
-               adr += PAGE_SIZE;
-               size -= PAGE_SIZE;
-       }
-       vfree(mem);
-}
+static char *mode_option = NULL;
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Preferred video mode (e.g. 640x480-8@60)");
 
-static struct fb_var_screeninfo vfb_default = {
+static const struct fb_videomode vfb_default = {
        .xres =         640,
        .yres =         480,
-       .xres_virtual = 640,
-       .yres_virtual = 480,
-       .bits_per_pixel = 8,
-       .red =          { 0, 8, 0 },
-       .green =        { 0, 8, 0 },
-       .blue =         { 0, 8, 0 },
-       .activate =     FB_ACTIVATE_TEST,
-       .height =       -1,
-       .width =        -1,
-       .pixclock =     20000,
-       .left_margin =  64,
-       .right_margin = 64,
-       .upper_margin = 32,
-       .lower_margin = 32,
-       .hsync_len =    64,
-       .vsync_len =    2,
-       .vmode =        FB_VMODE_NONINTERLACED,
+       .pixclock =     20000,
+       .left_margin =  64,
+       .right_margin = 64,
+       .upper_margin = 32,
+       .lower_margin = 32,
+       .hsync_len =    64,
+       .vsync_len =    2,
+       .vmode =        FB_VMODE_NONINTERLACED,
 };
 
 static struct fb_fix_screeninfo vfb_fix = {
@@ -119,6 +66,7 @@ static struct fb_fix_screeninfo vfb_fix = {
 
 static bool vfb_enable __initdata = 0; /* disabled by default */
 module_param(vfb_enable, bool, 0);
+MODULE_PARM_DESC(vfb_enable, "Enable Virtual FB driver");
 
 static int vfb_check_var(struct fb_var_screeninfo *var,
                         struct fb_info *info);
@@ -421,35 +369,7 @@ static int vfb_pan_display(struct fb_var_screeninfo *var,
 static int vfb_mmap(struct fb_info *info,
                    struct vm_area_struct *vma)
 {
-       unsigned long start = vma->vm_start;
-       unsigned long size = vma->vm_end - vma->vm_start;
-       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-       unsigned long page, pos;
-
-       if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
-               return -EINVAL;
-       if (size > info->fix.smem_len)
-               return -EINVAL;
-       if (offset > info->fix.smem_len - size)
-               return -EINVAL;
-
-       pos = (unsigned long)info->fix.smem_start + offset;
-
-       while (size > 0) {
-               page = vmalloc_to_pfn((void *)pos);
-               if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
-                       return -EAGAIN;
-               }
-               start += PAGE_SIZE;
-               pos += PAGE_SIZE;
-               if (size > PAGE_SIZE)
-                       size -= PAGE_SIZE;
-               else
-                       size = 0;
-       }
-
-       return 0;
-
+       return remap_vmalloc_range(vma, (void *)info->fix.smem_start, vma->vm_pgoff);
 }
 
 #ifndef MODULE
@@ -477,6 +397,8 @@ static int __init vfb_setup(char *options)
                /* Test disable for backwards compatibility */
                if (!strcmp(this_opt, "disable"))
                        vfb_enable = 0;
+               else
+                       mode_option = this_opt;
        }
        return 1;
 }
@@ -489,12 +411,13 @@ static int __init vfb_setup(char *options)
 static int vfb_probe(struct platform_device *dev)
 {
        struct fb_info *info;
+       unsigned int size = PAGE_ALIGN(videomemorysize);
        int retval = -ENOMEM;
 
        /*
         * For real video cards we use ioremap.
         */
-       if (!(videomemory = rvmalloc(videomemorysize)))
+       if (!(videomemory = vmalloc_32_user(size)))
                return retval;
 
        info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev);
@@ -504,11 +427,13 @@ static int vfb_probe(struct platform_device *dev)
        info->screen_base = (char __iomem *)videomemory;
        info->fbops = &vfb_ops;
 
-       retval = fb_find_mode(&info->var, info, NULL,
-                             NULL, 0, NULL, 8);
+       if (!fb_find_mode(&info->var, info, mode_option,
+                         NULL, 0, &vfb_default, 8)){
+               fb_err(info, "Unable to find usable video mode.\n");
+               retval = -EINVAL;
+               goto err1;
+       }
 
-       if (!retval || (retval == 4))
-               info->var = vfb_default;
        vfb_fix.smem_start = (unsigned long) videomemory;
        vfb_fix.smem_len = videomemorysize;
        info->fix = vfb_fix;
@@ -533,7 +458,7 @@ err2:
 err1:
        framebuffer_release(info);
 err:
-       rvfree(videomemory, videomemorysize);
+       vfree(videomemory);
        return retval;
 }
 
@@ -543,7 +468,7 @@ static int vfb_remove(struct platform_device *dev)
 
        if (info) {
                unregister_framebuffer(info);
-               rvfree(videomemory, videomemorysize);
+               vfree(videomemory);
                fb_dealloc_cmap(&info->cmap);
                framebuffer_release(info);
        }
index 283d335a759fef2766b4abcfa77a6b8cecebc7dc..5f0690c8fc936b0f2e20180c6ac0556647a0c846 100644 (file)
@@ -85,7 +85,7 @@ static struct fb_var_screeninfo vga16fb_defined = {
 };
 
 /* name should not depend on EGA/VGA */
-static struct fb_fix_screeninfo vga16fb_fix = {
+static const struct fb_fix_screeninfo vga16fb_fix = {
        .id             = "VGA16 VGA",
        .smem_start     = VGA_FB_PHYS,
        .smem_len       = VGA_FB_PHYS_LEN,
index 60bdad3a689b8280b373164f7a13fdcb2a5a1cc2..150ce2abf6c8f193b4e3b5c9fa66001cd709f39e 100644 (file)
@@ -245,8 +245,8 @@ static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p)
        /* Get the physical addresses of the source buffer */
        down_read(&current->mm->mmap_sem);
        num_pinned = get_user_pages(param.local_vaddr - lb_offset,
-               num_pages, (param.source == -1) ? READ : WRITE,
-               0, pages, NULL);
+               num_pages, (param.source == -1) ? 0 : FOLL_WRITE,
+               pages, NULL);
        up_read(&current->mm->mmap_sem);
 
        if (num_pinned != num_pages) {
index 50dbaa8056581532a7fc535308792782cadb2e1f..fdd3228e06781c4dd150091673e51ba3bdb9ce71 100644 (file)
@@ -1844,4 +1844,53 @@ config USBPCWATCHDOG
 
          Most people will say N.
 
+comment "Watchdog Pretimeout Governors"
+
+config WATCHDOG_PRETIMEOUT_GOV
+       bool "Enable watchdog pretimeout governors"
+       help
+         The option allows to select watchdog pretimeout governors.
+
+if WATCHDOG_PRETIMEOUT_GOV
+
+choice
+       prompt "Default Watchdog Pretimeout Governor"
+       default WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC
+       help
+         This option selects a default watchdog pretimeout governor.
+         The governor takes its action, if a watchdog is capable
+         to report a pretimeout event.
+
+config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP
+       bool "noop"
+       select WATCHDOG_PRETIMEOUT_GOV_NOOP
+       help
+         Use noop watchdog pretimeout governor by default. If noop
+         governor is selected by a user, write a short message to
+         the kernel log buffer and don't do any system changes.
+
+config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC
+       bool "panic"
+       select WATCHDOG_PRETIMEOUT_GOV_PANIC
+       help
+         Use panic watchdog pretimeout governor by default, if
+         a watchdog pretimeout event happens, consider that
+         a watchdog feeder is dead and reboot is unavoidable.
+
+endchoice
+
+config WATCHDOG_PRETIMEOUT_GOV_NOOP
+       tristate "Noop watchdog pretimeout governor"
+       help
+         Noop watchdog pretimeout governor, only an informational
+         message is added to kernel log buffer.
+
+config WATCHDOG_PRETIMEOUT_GOV_PANIC
+       tristate "Panic watchdog pretimeout governor"
+       help
+         Panic watchdog pretimeout governor, on watchdog pretimeout
+         event put the kernel into panic.
+
+endif # WATCHDOG_PRETIMEOUT_GOV
+
 endif # WATCHDOG
index cba00430151bbba3549dabcfff54bac0bed0bca1..caa9f4aa492ad27e4d1a1c0711d19bfc1906a5c3 100644 (file)
@@ -3,9 +3,15 @@
 #
 
 # The WatchDog Timer Driver Core.
-watchdog-objs  += watchdog_core.o watchdog_dev.o
 obj-$(CONFIG_WATCHDOG_CORE)    += watchdog.o
 
+watchdog-objs  += watchdog_core.o watchdog_dev.o
+
+watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV)     += watchdog_pretimeout.o
+
+obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP)     += pretimeout_noop.o
+obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC)    += pretimeout_panic.o
+
 # Only one watchdog can succeed. We probe the ISA/PCI/USB based
 # watchdog-cards first, then the architecture specific watchdog
 # drivers and then the architecture independent "softdog" driver.
index c9686b2fdafd7b84c64565abcf187cffef8acd7b..d0b59ba0f661a0be2dbfce74cc35851d08463ed5 100644 (file)
@@ -389,7 +389,6 @@ MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
 static struct platform_driver asm9260_wdt_driver = {
        .driver = {
                .name = "asm9260-wdt",
-               .owner = THIS_MODULE,
                .of_match_table = asm9260_wdt_of_match,
        },
        .probe = asm9260_wdt_probe,
index 4245b65d645cf5e91fa4864f6717bf7ae06dc21c..e238df4d75a23fdbd54d7e242452d8a6f5c8d1fd 100644 (file)
@@ -107,7 +107,7 @@ static struct watchdog_info bcm7038_wdt_info = {
                                WDIOF_MAGICCLOSE
 };
 
-static struct watchdog_ops bcm7038_wdt_ops = {
+static const struct watchdog_ops bcm7038_wdt_ops = {
        .owner          = THIS_MODULE,
        .start          = bcm7038_wdt_start,
        .stop           = bcm7038_wdt_stop,
index 4dda9024e2298f954bb5934de05c00910fdf4bab..98acef72334d7760296ab98ef30fa1d417a6ad83 100644 (file)
@@ -269,7 +269,7 @@ static struct watchdog_info cdns_wdt_info = {
 };
 
 /* Watchdog Core Ops */
-static struct watchdog_ops cdns_wdt_ops = {
+static const struct watchdog_ops cdns_wdt_ops = {
        .owner = THIS_MODULE,
        .start = cdns_wdt_start,
        .stop = cdns_wdt_stop,
@@ -424,8 +424,10 @@ static int __maybe_unused cdns_wdt_suspend(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct cdns_wdt *wdt = platform_get_drvdata(pdev);
 
-       cdns_wdt_stop(&wdt->cdns_wdt_device);
-       clk_disable_unprepare(wdt->clk);
+       if (watchdog_active(&wdt->cdns_wdt_device)) {
+               cdns_wdt_stop(&wdt->cdns_wdt_device);
+               clk_disable_unprepare(wdt->clk);
+       }
 
        return 0;
 }
@@ -442,12 +444,14 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct cdns_wdt *wdt = platform_get_drvdata(pdev);
 
-       ret = clk_prepare_enable(wdt->clk);
-       if (ret) {
-               dev_err(dev, "unable to enable clock\n");
-               return ret;
+       if (watchdog_active(&wdt->cdns_wdt_device)) {
+               ret = clk_prepare_enable(wdt->clk);
+               if (ret) {
+                       dev_err(dev, "unable to enable clock\n");
+                       return ret;
+               }
+               cdns_wdt_start(&wdt->cdns_wdt_device);
        }
-       cdns_wdt_start(&wdt->cdns_wdt_device);
 
        return 0;
 }
index 2acb51cf55041dc2b71e677e9820d885217bdf69..3c6a3de13a1bc1cf06c3cc2a85f9489227952c4f 100644 (file)
@@ -54,6 +54,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
 struct dw_wdt {
        void __iomem            *regs;
        struct clk              *clk;
+       unsigned long           rate;
        struct notifier_block   restart_handler;
        struct watchdog_device  wdd;
 };
@@ -72,7 +73,7 @@ static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top)
         * There are 16 possible timeout values in 0..15 where the number of
         * cycles is 2 ^ (16 + i) and the watchdog counts down.
         */
-       return (1U << (16 + top)) / clk_get_rate(dw_wdt->clk);
+       return (1U << (16 + top)) / dw_wdt->rate;
 }
 
 static int dw_wdt_get_top(struct dw_wdt *dw_wdt)
@@ -163,7 +164,7 @@ static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd)
        struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
 
        return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
-               clk_get_rate(dw_wdt->clk);
+               dw_wdt->rate;
 }
 
 static const struct watchdog_info dw_wdt_ident = {
@@ -231,6 +232,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
+       dw_wdt->rate = clk_get_rate(dw_wdt->clk);
+       if (dw_wdt->rate == 0) {
+               ret = -EINVAL;
+               goto out_disable_clk;
+       }
+
        wdd = &dw_wdt->wdd;
        wdd->info = &dw_wdt_ident;
        wdd->ops = &dw_wdt_ops;
index 8f89bd8a826a994b2076733c3b0d8b03869977fd..70c7194e2810dff05ddc0535dc5d8d7579ab7e77 100644 (file)
@@ -39,7 +39,7 @@
 #include <asm/nmi.h>
 #include <asm/frame.h>
 
-#define HPWDT_VERSION                  "1.3.3"
+#define HPWDT_VERSION                  "1.4.0"
 #define SECS_TO_TICKS(secs)            ((secs) * 1000 / 128)
 #define TICKS_TO_SECS(ticks)           ((ticks) * 128 / 1000)
 #define HPWDT_MAX_TIMER                        TICKS_TO_SECS(65535)
@@ -814,7 +814,8 @@ static int hpwdt_init_one(struct pci_dev *dev,
         * not run on a legacy ASM box.
         * So we only support the G5 ProLiant servers and higher.
         */
-       if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) {
+       if (dev->subsystem_vendor != PCI_VENDOR_ID_HP &&
+           dev->subsystem_vendor != PCI_VENDOR_ID_HP_3PAR) {
                dev_warn(&dev->dev,
                        "This server does not have an iLO2+ ASIC.\n");
                return -ENODEV;
@@ -823,7 +824,8 @@ static int hpwdt_init_one(struct pci_dev *dev,
        /*
         * Ignore all auxilary iLO devices with the following PCI ID
         */
-       if (dev->subsystem_device == 0x1979)
+       if (dev->subsystem_vendor == PCI_VENDOR_ID_HP &&
+           dev->subsystem_device == 0x1979)
                return -ENODEV;
 
        if (pci_enable_device(dev)) {
index 54cab189a763ff2ec4abeb218cee01e03ec5c608..06fcb6c8c9172379dd320aaaed10f368cdced739 100644 (file)
@@ -629,7 +629,7 @@ static int iTCO_wdt_resume_noirq(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops iTCO_wdt_pm = {
+static const struct dev_pm_ops iTCO_wdt_pm = {
        .suspend_noirq = iTCO_wdt_suspend_noirq,
        .resume_noirq = iTCO_wdt_resume_noirq,
 };
index 62f346bb43484282aa7bd2665ddf62efb473eda0..4874b0f18650f7bae4380f4a34b9ad5203ae49d5 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 
 #define IMX2_WDT_WCR           0x00            /* Control Register */
 #define IMX2_WDT_WCR_WT                (0xFF << 8)     /* -> Watchdog Timeout Field */
-#define IMX2_WDT_WCR_WDA       (1 << 5)        /* -> External Reset WDOG_B */
-#define IMX2_WDT_WCR_SRS       (1 << 4)        /* -> Software Reset Signal */
-#define IMX2_WDT_WCR_WRE       (1 << 3)        /* -> WDOG Reset Enable */
-#define IMX2_WDT_WCR_WDE       (1 << 2)        /* -> Watchdog Enable */
-#define IMX2_WDT_WCR_WDZST     (1 << 0)        /* -> Watchdog timer Suspend */
+#define IMX2_WDT_WCR_WDA       BIT(5)          /* -> External Reset WDOG_B */
+#define IMX2_WDT_WCR_SRS       BIT(4)          /* -> Software Reset Signal */
+#define IMX2_WDT_WCR_WRE       BIT(3)          /* -> WDOG Reset Enable */
+#define IMX2_WDT_WCR_WDE       BIT(2)          /* -> Watchdog Enable */
+#define IMX2_WDT_WCR_WDZST     BIT(0)          /* -> Watchdog timer Suspend */
 
 #define IMX2_WDT_WSR           0x02            /* Service Register */
 #define IMX2_WDT_SEQ1          0x5555          /* -> service sequence 1 */
 #define IMX2_WDT_SEQ2          0xAAAA          /* -> service sequence 2 */
 
 #define IMX2_WDT_WRSR          0x04            /* Reset Status Register */
-#define IMX2_WDT_WRSR_TOUT     (1 << 1)        /* -> Reset due to Timeout */
+#define IMX2_WDT_WRSR_TOUT     BIT(1)          /* -> Reset due to Timeout */
+
+#define IMX2_WDT_WICR          0x06            /* Interrupt Control Register */
+#define IMX2_WDT_WICR_WIE      BIT(15)         /* -> Interrupt Enable */
+#define IMX2_WDT_WICR_WTIS     BIT(14)         /* -> Interrupt Status */
+#define IMX2_WDT_WICR_WICT     0xFF            /* -> Interrupt Count Timeout */
 
 #define IMX2_WDT_WMCR          0x08            /* Misc Register */
 
@@ -80,6 +86,12 @@ static const struct watchdog_info imx2_wdt_info = {
        .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
 };
 
+static const struct watchdog_info imx2_wdt_pretimeout_info = {
+       .identity = "imx2+ watchdog",
+       .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+                  WDIOF_PRETIMEOUT,
+};
+
 static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action,
                            void *data)
 {
@@ -169,6 +181,35 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
        return 0;
 }
 
+static int imx2_wdt_set_pretimeout(struct watchdog_device *wdog,
+                                  unsigned int new_pretimeout)
+{
+       struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+       if (new_pretimeout >= IMX2_WDT_MAX_TIME)
+               return -EINVAL;
+
+       wdog->pretimeout = new_pretimeout;
+
+       regmap_update_bits(wdev->regmap, IMX2_WDT_WICR,
+                          IMX2_WDT_WICR_WIE | IMX2_WDT_WICR_WICT,
+                          IMX2_WDT_WICR_WIE | (new_pretimeout << 1));
+       return 0;
+}
+
+static irqreturn_t imx2_wdt_isr(int irq, void *wdog_arg)
+{
+       struct watchdog_device *wdog = wdog_arg;
+       struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+       regmap_write_bits(wdev->regmap, IMX2_WDT_WICR,
+                         IMX2_WDT_WICR_WTIS, IMX2_WDT_WICR_WTIS);
+
+       watchdog_notify_pretimeout(wdog);
+
+       return IRQ_HANDLED;
+}
+
 static int imx2_wdt_start(struct watchdog_device *wdog)
 {
        struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
@@ -188,6 +229,7 @@ static const struct watchdog_ops imx2_wdt_ops = {
        .start = imx2_wdt_start,
        .ping = imx2_wdt_ping,
        .set_timeout = imx2_wdt_set_timeout,
+       .set_pretimeout = imx2_wdt_set_pretimeout,
        .restart = imx2_wdt_restart,
 };
 
@@ -236,6 +278,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
        wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000;
        wdog->parent            = &pdev->dev;
 
+       ret = platform_get_irq(pdev, 0);
+       if (ret > 0)
+               if (!devm_request_irq(&pdev->dev, ret, imx2_wdt_isr, 0,
+                                     dev_name(&pdev->dev), wdog))
+                       wdog->info = &imx2_wdt_pretimeout_info;
+
        ret = clk_prepare_enable(wdev->clk);
        if (ret)
                return ret;
index 5bf931ce13538b0137b9d27556911b283e1848a4..8e302d0e346c9b6a617fd0c1f3a64c65f0a084df 100644 (file)
@@ -430,7 +430,7 @@ static struct watchdog_info kempld_wdt_info = {
                        WDIOF_PRETIMEOUT
 };
 
-static struct watchdog_ops kempld_wdt_ops = {
+static const struct watchdog_ops kempld_wdt_ops = {
        .owner          = THIS_MODULE,
        .start          = kempld_wdt_start,
        .stop           = kempld_wdt_stop,
index 4a2290f900a87db3c06df2079a8439b1caa7e240..d5735c12067d609465c556e17422e86b08ffe140 100644 (file)
@@ -139,7 +139,6 @@ static int mt7621_wdt_probe(struct platform_device *pdev)
        if (!IS_ERR(mt7621_wdt_reset))
                reset_control_deassert(mt7621_wdt_reset);
 
-       mt7621_wdt_dev.dev = &pdev->dev;
        mt7621_wdt_dev.bootstatus = mt7621_wdt_bootcause();
 
        watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout,
index b2e1b4cbbdc1a18d419ce0e5eb56b92e14445872..fae7fe929ea38c299352cac82db0b161167f58f4 100644 (file)
@@ -10,6 +10,7 @@
  * 2 of the License, or (at your option) any later version.
  */
 
+#include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/types.h>
@@ -45,6 +46,7 @@ struct xwdt_device {
        u32 wdt_interval;
        spinlock_t spinlock;
        struct watchdog_device xilinx_wdt_wdd;
+       struct clk              *clk;
 };
 
 static int xilinx_wdt_start(struct watchdog_device *wdd)
@@ -195,16 +197,30 @@ static int xwdt_probe(struct platform_device *pdev)
        spin_lock_init(&xdev->spinlock);
        watchdog_set_drvdata(xilinx_wdt_wdd, xdev);
 
+       xdev->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(xdev->clk)) {
+               if (PTR_ERR(xdev->clk) == -ENOENT)
+                       xdev->clk = NULL;
+               else
+                       return PTR_ERR(xdev->clk);
+       }
+
+       rc = clk_prepare_enable(xdev->clk);
+       if (rc) {
+               dev_err(&pdev->dev, "unable to enable clock\n");
+               return rc;
+       }
+
        rc = xwdt_selftest(xdev);
        if (rc == XWT_TIMER_FAILED) {
                dev_err(&pdev->dev, "SelfTest routine error\n");
-               return rc;
+               goto err_clk_disable;
        }
 
        rc = watchdog_register_device(xilinx_wdt_wdd);
        if (rc) {
                dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc);
-               return rc;
+               goto err_clk_disable;
        }
 
        dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n",
@@ -213,6 +229,10 @@ static int xwdt_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, xdev);
 
        return 0;
+err_clk_disable:
+       clk_disable_unprepare(xdev->clk);
+
+       return rc;
 }
 
 static int xwdt_remove(struct platform_device *pdev)
@@ -220,6 +240,7 @@ static int xwdt_remove(struct platform_device *pdev)
        struct xwdt_device *xdev = platform_get_drvdata(pdev);
 
        watchdog_unregister_device(&xdev->xilinx_wdt_wdd);
+       clk_disable_unprepare(xdev->clk);
 
        return 0;
 }
diff --git a/drivers/watchdog/pretimeout_noop.c b/drivers/watchdog/pretimeout_noop.c
new file mode 100644 (file)
index 0000000..85f5299
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015-2016 Mentor Graphics
+ *
+ * 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/printk.h>
+#include <linux/watchdog.h>
+
+#include "watchdog_pretimeout.h"
+
+/**
+ * pretimeout_noop - No operation on watchdog pretimeout event
+ * @wdd - watchdog_device
+ *
+ * This function prints a message about pretimeout to kernel log.
+ */
+static void pretimeout_noop(struct watchdog_device *wdd)
+{
+       pr_alert("watchdog%d: pretimeout event\n", wdd->id);
+}
+
+static struct watchdog_governor watchdog_gov_noop = {
+       .name           = "noop",
+       .pretimeout     = pretimeout_noop,
+};
+
+static int __init watchdog_gov_noop_register(void)
+{
+       return watchdog_register_governor(&watchdog_gov_noop);
+}
+
+static void __exit watchdog_gov_noop_unregister(void)
+{
+       watchdog_unregister_governor(&watchdog_gov_noop);
+}
+module_init(watchdog_gov_noop_register);
+module_exit(watchdog_gov_noop_unregister);
+
+MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>");
+MODULE_DESCRIPTION("Panic watchdog pretimeout governor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/pretimeout_panic.c b/drivers/watchdog/pretimeout_panic.c
new file mode 100644 (file)
index 0000000..0c197a1
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015-2016 Mentor Graphics
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+
+#include "watchdog_pretimeout.h"
+
+/**
+ * pretimeout_panic - Panic on watchdog pretimeout event
+ * @wdd - watchdog_device
+ *
+ * Panic, watchdog has not been fed till pretimeout event.
+ */
+static void pretimeout_panic(struct watchdog_device *wdd)
+{
+       panic("watchdog pretimeout event\n");
+}
+
+static struct watchdog_governor watchdog_gov_panic = {
+       .name           = "panic",
+       .pretimeout     = pretimeout_panic,
+};
+
+static int __init watchdog_gov_panic_register(void)
+{
+       return watchdog_register_governor(&watchdog_gov_panic);
+}
+
+static void __exit watchdog_gov_panic_unregister(void)
+{
+       watchdog_unregister_governor(&watchdog_gov_panic);
+}
+module_init(watchdog_gov_panic_register);
+module_exit(watchdog_gov_panic_unregister);
+
+MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>");
+MODULE_DESCRIPTION("Panic watchdog pretimeout governor");
+MODULE_LICENSE("GPL");
index d1c12278cb6a1b3eb43542460d24f2f4eb581af8..0805ee2acd7a94bb913472f5bd33bea5d95bc0c8 100644 (file)
@@ -136,7 +136,7 @@ static struct watchdog_info rn5t618_wdt_info = {
        .identity       = DRIVER_NAME,
 };
 
-static struct watchdog_ops rn5t618_wdt_ops = {
+static const struct watchdog_ops rn5t618_wdt_ops = {
        .owner          = THIS_MODULE,
        .start          = rn5t618_wdt_start,
        .stop           = rn5t618_wdt_stop,
index 1967919ae74330454a440925689f6de95fcdb65c..14b4fd428fffbbfaf539eb0ab06273858d15ed93 100644 (file)
@@ -158,7 +158,6 @@ static int rt288x_wdt_probe(struct platform_device *pdev)
 
        rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE;
 
-       rt288x_wdt_dev.dev = &pdev->dev;
        rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
        rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
        rt288x_wdt_dev.parent = &pdev->dev;
index b067edf246dff2e594429b36d968b698bcd71e60..c7bdc986dca1c249c2b61ad902504cdd1df4fb2d 100644 (file)
@@ -72,10 +72,27 @@ static void softdog_fire(unsigned long data)
 static struct timer_list softdog_ticktock =
                TIMER_INITIALIZER(softdog_fire, 0, 0);
 
+static struct watchdog_device softdog_dev;
+
+static void softdog_pretimeout(unsigned long data)
+{
+       watchdog_notify_pretimeout(&softdog_dev);
+}
+
+static struct timer_list softdog_preticktock =
+               TIMER_INITIALIZER(softdog_pretimeout, 0, 0);
+
 static int softdog_ping(struct watchdog_device *w)
 {
        if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ)))
                __module_get(THIS_MODULE);
+
+       if (w->pretimeout)
+               mod_timer(&softdog_preticktock, jiffies +
+                         (w->timeout - w->pretimeout) * HZ);
+       else
+               del_timer(&softdog_preticktock);
+
        return 0;
 }
 
@@ -84,15 +101,18 @@ static int softdog_stop(struct watchdog_device *w)
        if (del_timer(&softdog_ticktock))
                module_put(THIS_MODULE);
 
+       del_timer(&softdog_preticktock);
+
        return 0;
 }
 
 static struct watchdog_info softdog_info = {
        .identity = "Software Watchdog",
-       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
+                  WDIOF_PRETIMEOUT,
 };
 
-static struct watchdog_ops softdog_ops = {
+static const struct watchdog_ops softdog_ops = {
        .owner = THIS_MODULE,
        .start = softdog_ping,
        .stop = softdog_stop,
index 14e9badf2bfa37b9c8fa0d3d923ecaa188b987b8..e6100e447dd89f391a68575c0b8542b6f8d3f513 100644 (file)
@@ -52,27 +52,6 @@ struct st_wdog {
        bool warm_reset;
 };
 
-static struct st_wdog_syscfg stid127_syscfg = {
-       .reset_type_reg         = 0x004,
-       .reset_type_mask        = BIT(2),
-       .enable_reg             = 0x000,
-       .enable_mask            = BIT(2),
-};
-
-static struct st_wdog_syscfg stih415_syscfg = {
-       .reset_type_reg         = 0x0B8,
-       .reset_type_mask        = BIT(6),
-       .enable_reg             = 0x0B4,
-       .enable_mask            = BIT(7),
-};
-
-static struct st_wdog_syscfg stih416_syscfg = {
-       .reset_type_reg         = 0x88C,
-       .reset_type_mask        = BIT(6),
-       .enable_reg             = 0x888,
-       .enable_mask            = BIT(7),
-};
-
 static struct st_wdog_syscfg stih407_syscfg = {
        .enable_reg             = 0x204,
        .enable_mask            = BIT(19),
@@ -83,18 +62,6 @@ static const struct of_device_id st_wdog_match[] = {
                .compatible = "st,stih407-lpc",
                .data = &stih407_syscfg,
        },
-       {
-               .compatible = "st,stih416-lpc",
-               .data = &stih416_syscfg,
-       },
-       {
-               .compatible = "st,stih415-lpc",
-               .data = &stih415_syscfg,
-       },
-       {
-               .compatible = "st,stid127-lpc",
-               .data = &stid127_syscfg,
-       },
        {},
 };
 MODULE_DEVICE_TABLE(of, st_wdog_match);
index 9ec57608da82931e6f2a913246825ff9be11e034..2d53c3f9394f2a39bf78175bf4c229c7f3bbab90 100644 (file)
@@ -178,7 +178,7 @@ static const struct watchdog_info tegra_wdt_info = {
        .identity       = "Tegra Watchdog",
 };
 
-static struct watchdog_ops tegra_wdt_ops = {
+static const struct watchdog_ops tegra_wdt_ops = {
        .owner = THIS_MODULE,
        .start = tegra_wdt_start,
        .stop = tegra_wdt_stop,
index c2da880292bc2f326b71678634de5d2968bd38c8..6f7a9deb27d05d25d9a261d9c69460c2dccb9528 100644 (file)
@@ -112,7 +112,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
                txx9_imclk = NULL;
                goto exit;
        }
-       ret = clk_enable(txx9_imclk);
+       ret = clk_prepare_enable(txx9_imclk);
        if (ret) {
                clk_put(txx9_imclk);
                txx9_imclk = NULL;
@@ -144,7 +144,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
        return 0;
 exit:
        if (txx9_imclk) {
-               clk_disable(txx9_imclk);
+               clk_disable_unprepare(txx9_imclk);
                clk_put(txx9_imclk);
        }
        return ret;
@@ -153,7 +153,7 @@ exit:
 static int __exit txx9wdt_remove(struct platform_device *dev)
 {
        watchdog_unregister_device(&txx9wdt);
-       clk_disable(txx9_imclk);
+       clk_disable_unprepare(txx9_imclk);
        clk_put(txx9_imclk);
        return 0;
 }
index 09e8003039dc5bfa246698a15eaf6bf5150ed05d..ef2ecaf53a147e54ca96c862b43e4c87e59903b6 100644 (file)
@@ -302,7 +302,7 @@ static struct watchdog_info wdt_info = {
        .identity = "W83627HF Watchdog",
 };
 
-static struct watchdog_ops wdt_ops = {
+static const struct watchdog_ops wdt_ops = {
        .owner = THIS_MODULE,
        .start = wdt_start,
        .stop = wdt_stop,
index 6abb83cd7681034db7b3ca64ef41d7e6455112c2..74265b2f806ca24d4eda05ef860e8092ef1afe1d 100644 (file)
@@ -349,7 +349,7 @@ int devm_watchdog_register_device(struct device *dev,
        struct watchdog_device **rcwdd;
        int ret;
 
-       rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*wdd),
+       rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*rcwdd),
                             GFP_KERNEL);
        if (!rcwdd)
                return -ENOMEM;
index 040bf8382f46cd9c40673412339a8da672887594..32930a073a12744b6d6cf1bcc0b154effea431ab 100644 (file)
@@ -49,6 +49,7 @@
 #include <linux/uaccess.h>     /* For copy_to_user/put_user/... */
 
 #include "watchdog_core.h"
+#include "watchdog_pretimeout.h"
 
 /*
  * struct watchdog_core_data - watchdog core internal data
@@ -335,16 +336,45 @@ static int watchdog_set_timeout(struct watchdog_device *wdd,
        if (watchdog_timeout_invalid(wdd, timeout))
                return -EINVAL;
 
-       if (wdd->ops->set_timeout)
+       if (wdd->ops->set_timeout) {
                err = wdd->ops->set_timeout(wdd, timeout);
-       else
+       } else {
                wdd->timeout = timeout;
+               /* Disable pretimeout if it doesn't fit the new timeout */
+               if (wdd->pretimeout >= wdd->timeout)
+                       wdd->pretimeout = 0;
+       }
 
        watchdog_update_worker(wdd);
 
        return err;
 }
 
+/*
+ *     watchdog_set_pretimeout: set the watchdog timer pretimeout
+ *     @wdd: the watchdog device to set the timeout for
+ *     @timeout: pretimeout to set in seconds
+ */
+
+static int watchdog_set_pretimeout(struct watchdog_device *wdd,
+                                  unsigned int timeout)
+{
+       int err = 0;
+
+       if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+               return -EOPNOTSUPP;
+
+       if (watchdog_pretimeout_invalid(wdd, timeout))
+               return -EINVAL;
+
+       if (wdd->ops->set_pretimeout)
+               err = wdd->ops->set_pretimeout(wdd, timeout);
+       else
+               wdd->pretimeout = timeout;
+
+       return err;
+}
+
 /*
  *     watchdog_get_timeleft: wrapper to get the time left before a reboot
  *     @wdd: the watchdog device to get the remaining time from
@@ -429,6 +459,15 @@ static ssize_t timeout_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(timeout);
 
+static ssize_t pretimeout_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct watchdog_device *wdd = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", wdd->pretimeout);
+}
+static DEVICE_ATTR_RO(pretimeout);
+
 static ssize_t identity_show(struct device *dev, struct device_attribute *attr,
                                char *buf)
 {
@@ -450,6 +489,36 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(state);
 
+static ssize_t pretimeout_available_governors_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       return watchdog_pretimeout_available_governors_get(buf);
+}
+static DEVICE_ATTR_RO(pretimeout_available_governors);
+
+static ssize_t pretimeout_governor_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct watchdog_device *wdd = dev_get_drvdata(dev);
+
+       return watchdog_pretimeout_governor_get(wdd, buf);
+}
+
+static ssize_t pretimeout_governor_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t count)
+{
+       struct watchdog_device *wdd = dev_get_drvdata(dev);
+       int ret = watchdog_pretimeout_governor_set(wdd, buf);
+
+       if (!ret)
+               ret = count;
+
+       return ret;
+}
+static DEVICE_ATTR_RW(pretimeout_governor);
+
 static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
                                int n)
 {
@@ -459,6 +528,14 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
 
        if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
                mode = 0;
+       else if (attr == &dev_attr_pretimeout.attr &&
+                !(wdd->info->options & WDIOF_PRETIMEOUT))
+               mode = 0;
+       else if ((attr == &dev_attr_pretimeout_governor.attr ||
+                 attr == &dev_attr_pretimeout_available_governors.attr) &&
+                (!(wdd->info->options & WDIOF_PRETIMEOUT) ||
+                 !IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)))
+               mode = 0;
 
        return mode;
 }
@@ -466,10 +543,13 @@ static struct attribute *wdt_attrs[] = {
        &dev_attr_state.attr,
        &dev_attr_identity.attr,
        &dev_attr_timeout.attr,
+       &dev_attr_pretimeout.attr,
        &dev_attr_timeleft.attr,
        &dev_attr_bootstatus.attr,
        &dev_attr_status.attr,
        &dev_attr_nowayout.attr,
+       &dev_attr_pretimeout_governor.attr,
+       &dev_attr_pretimeout_available_governors.attr,
        NULL,
 };
 
@@ -646,6 +726,16 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
                        break;
                err = put_user(val, p);
                break;
+       case WDIOC_SETPRETIMEOUT:
+               if (get_user(val, p)) {
+                       err = -EFAULT;
+                       break;
+               }
+               err = watchdog_set_pretimeout(wdd, val);
+               break;
+       case WDIOC_GETPRETIMEOUT:
+               err = put_user(wdd->pretimeout, p);
+               break;
        default:
                err = -ENOTTY;
                break;
@@ -937,6 +1027,12 @@ int watchdog_dev_register(struct watchdog_device *wdd)
                return PTR_ERR(dev);
        }
 
+       ret = watchdog_register_pretimeout(wdd);
+       if (ret) {
+               device_destroy(&watchdog_class, devno);
+               watchdog_cdev_unregister(wdd);
+       }
+
        return ret;
 }
 
@@ -950,6 +1046,7 @@ int watchdog_dev_register(struct watchdog_device *wdd)
 
 void watchdog_dev_unregister(struct watchdog_device *wdd)
 {
+       watchdog_unregister_pretimeout(wdd);
        device_destroy(&watchdog_class, wdd->wd_data->cdev.dev);
        watchdog_cdev_unregister(wdd);
 }
diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c
new file mode 100644 (file)
index 0000000..9db07bf
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2015-2016 Mentor Graphics
+ *
+ * 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/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/watchdog.h>
+
+#include "watchdog_pretimeout.h"
+
+/* Default watchdog pretimeout governor */
+static struct watchdog_governor *default_gov;
+
+/* The spinlock protects default_gov, wdd->gov and pretimeout_list */
+static DEFINE_SPINLOCK(pretimeout_lock);
+
+/* List of watchdog devices, which can generate a pretimeout event */
+static LIST_HEAD(pretimeout_list);
+
+struct watchdog_pretimeout {
+       struct watchdog_device          *wdd;
+       struct list_head                entry;
+};
+
+/* The mutex protects governor list and serializes external interfaces */
+static DEFINE_MUTEX(governor_lock);
+
+/* List of the registered watchdog pretimeout governors */
+static LIST_HEAD(governor_list);
+
+struct governor_priv {
+       struct watchdog_governor        *gov;
+       struct list_head                entry;
+};
+
+static struct governor_priv *find_governor_by_name(const char *gov_name)
+{
+       struct governor_priv *priv;
+
+       list_for_each_entry(priv, &governor_list, entry)
+               if (sysfs_streq(gov_name, priv->gov->name))
+                       return priv;
+
+       return NULL;
+}
+
+int watchdog_pretimeout_available_governors_get(char *buf)
+{
+       struct governor_priv *priv;
+       int count = 0;
+
+       mutex_lock(&governor_lock);
+
+       list_for_each_entry(priv, &governor_list, entry)
+               count += sprintf(buf + count, "%s\n", priv->gov->name);
+
+       mutex_unlock(&governor_lock);
+
+       return count;
+}
+
+int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf)
+{
+       int count = 0;
+
+       spin_lock_irq(&pretimeout_lock);
+       if (wdd->gov)
+               count = sprintf(buf, "%s\n", wdd->gov->name);
+       spin_unlock_irq(&pretimeout_lock);
+
+       return count;
+}
+
+int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
+                                    const char *buf)
+{
+       struct governor_priv *priv;
+
+       mutex_lock(&governor_lock);
+
+       priv = find_governor_by_name(buf);
+       if (!priv) {
+               mutex_unlock(&governor_lock);
+               return -EINVAL;
+       }
+
+       spin_lock_irq(&pretimeout_lock);
+       wdd->gov = priv->gov;
+       spin_unlock_irq(&pretimeout_lock);
+
+       mutex_unlock(&governor_lock);
+
+       return 0;
+}
+
+void watchdog_notify_pretimeout(struct watchdog_device *wdd)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&pretimeout_lock, flags);
+       if (!wdd->gov) {
+               spin_unlock_irqrestore(&pretimeout_lock, flags);
+               return;
+       }
+
+       wdd->gov->pretimeout(wdd);
+       spin_unlock_irqrestore(&pretimeout_lock, flags);
+}
+EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);
+
+int watchdog_register_governor(struct watchdog_governor *gov)
+{
+       struct watchdog_pretimeout *p;
+       struct governor_priv *priv;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       mutex_lock(&governor_lock);
+
+       if (find_governor_by_name(gov->name)) {
+               mutex_unlock(&governor_lock);
+               kfree(priv);
+               return -EBUSY;
+       }
+
+       priv->gov = gov;
+       list_add(&priv->entry, &governor_list);
+
+       if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV,
+                    WATCHDOG_GOV_NAME_MAXLEN)) {
+               spin_lock_irq(&pretimeout_lock);
+               default_gov = gov;
+
+               list_for_each_entry(p, &pretimeout_list, entry)
+                       if (!p->wdd->gov)
+                               p->wdd->gov = default_gov;
+               spin_unlock_irq(&pretimeout_lock);
+       }
+
+       mutex_unlock(&governor_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(watchdog_register_governor);
+
+void watchdog_unregister_governor(struct watchdog_governor *gov)
+{
+       struct watchdog_pretimeout *p;
+       struct governor_priv *priv, *t;
+
+       mutex_lock(&governor_lock);
+
+       list_for_each_entry_safe(priv, t, &governor_list, entry) {
+               if (priv->gov == gov) {
+                       list_del(&priv->entry);
+                       kfree(priv);
+                       break;
+               }
+       }
+
+       spin_lock_irq(&pretimeout_lock);
+       list_for_each_entry(p, &pretimeout_list, entry)
+               if (p->wdd->gov == gov)
+                       p->wdd->gov = default_gov;
+       spin_unlock_irq(&pretimeout_lock);
+
+       mutex_unlock(&governor_lock);
+}
+EXPORT_SYMBOL(watchdog_unregister_governor);
+
+int watchdog_register_pretimeout(struct watchdog_device *wdd)
+{
+       struct watchdog_pretimeout *p;
+
+       if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+               return 0;
+
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
+       spin_lock_irq(&pretimeout_lock);
+       list_add(&p->entry, &pretimeout_list);
+       p->wdd = wdd;
+       wdd->gov = default_gov;
+       spin_unlock_irq(&pretimeout_lock);
+
+       return 0;
+}
+
+void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
+{
+       struct watchdog_pretimeout *p, *t;
+
+       if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+               return;
+
+       spin_lock_irq(&pretimeout_lock);
+       wdd->gov = NULL;
+
+       list_for_each_entry_safe(p, t, &pretimeout_list, entry) {
+               if (p->wdd == wdd) {
+                       list_del(&p->entry);
+                       break;
+               }
+       }
+       spin_unlock_irq(&pretimeout_lock);
+
+       kfree(p);
+}
diff --git a/drivers/watchdog/watchdog_pretimeout.h b/drivers/watchdog/watchdog_pretimeout.h
new file mode 100644 (file)
index 0000000..a5a32b3
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef __WATCHDOG_PRETIMEOUT_H
+#define __WATCHDOG_PRETIMEOUT_H
+
+#define WATCHDOG_GOV_NAME_MAXLEN       20
+
+struct watchdog_device;
+
+struct watchdog_governor {
+       const char      name[WATCHDOG_GOV_NAME_MAXLEN];
+       void            (*pretimeout)(struct watchdog_device *wdd);
+};
+
+#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)
+/* Interfaces to watchdog pretimeout governors */
+int watchdog_register_governor(struct watchdog_governor *gov);
+void watchdog_unregister_governor(struct watchdog_governor *gov);
+
+/* Interfaces to watchdog_dev.c */
+int watchdog_register_pretimeout(struct watchdog_device *wdd);
+void watchdog_unregister_pretimeout(struct watchdog_device *wdd);
+int watchdog_pretimeout_available_governors_get(char *buf);
+int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf);
+int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
+                                    const char *buf);
+
+#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP)
+#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV                "noop"
+#elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC)
+#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV                "panic"
+#endif
+
+#else
+static inline int watchdog_register_pretimeout(struct watchdog_device *wdd)
+{
+       return 0;
+}
+
+static inline void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
+{
+}
+
+static inline int watchdog_pretimeout_available_governors_get(char *buf)
+{
+       return -EINVAL;
+}
+
+static inline int watchdog_pretimeout_governor_get(struct watchdog_device *wdd,
+                                                  char *buf)
+{
+       return -EINVAL;
+}
+
+static inline int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
+                                                  const char *buf)
+{
+       return -EINVAL;
+}
+#endif
+
+#endif
index fa1efef3c96e2c06aa0a5c10a7413e952bc262d6..b4e0cea5a64eec8c292762d3a390a492e447c282 100644 (file)
  * GNU General Public License for more details.
  */
 
+#include <linux/delay.h>
 #include <linux/i2c.h>
+#include <linux/ihex.h>
+#include <linux/firmware.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -36,6 +39,8 @@
 #define ZIIRAVE_STATE_OFF      0x1
 #define ZIIRAVE_STATE_ON       0x2
 
+#define ZIIRAVE_FW_NAME                "ziirave_wdt.fw"
+
 static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
                                  "host request", NULL, "illegal configuration",
                                  "illegal instruction", "illegal trap",
@@ -50,12 +55,35 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
 #define ZIIRAVE_WDT_PING               0x9
 #define ZIIRAVE_WDT_RESET_DURATION     0xa
 
+#define ZIIRAVE_FIRM_PKT_TOTAL_SIZE    20
+#define ZIIRAVE_FIRM_PKT_DATA_SIZE     16
+#define ZIIRAVE_FIRM_FLASH_MEMORY_START        0x1600
+#define ZIIRAVE_FIRM_FLASH_MEMORY_END  0x2bbf
+
+/* Received and ready for next Download packet. */
+#define ZIIRAVE_FIRM_DOWNLOAD_ACK      1
+/* Currently writing to flash. Retry Download status in a moment! */
+#define ZIIRAVE_FIRM_DOWNLOAD_BUSY     2
+
+/* Wait for ACK timeout in ms */
+#define ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT      50
+
+/* Firmware commands */
+#define ZIIRAVE_CMD_DOWNLOAD_START             0x10
+#define ZIIRAVE_CMD_DOWNLOAD_END               0x11
+#define ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR     0x12
+#define ZIIRAVE_CMD_DOWNLOAD_READ_BYTE         0x13
+#define ZIIRAVE_CMD_RESET_PROCESSOR            0x0b
+#define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER         0x0c
+#define ZIIRAVE_CMD_DOWNLOAD_PACKET            0x0e
+
 struct ziirave_wdt_rev {
        unsigned char major;
        unsigned char minor;
 };
 
 struct ziirave_wdt_data {
+       struct mutex sysfs_mutex;
        struct watchdog_device wdd;
        struct ziirave_wdt_rev bootloader_rev;
        struct ziirave_wdt_rev firmware_rev;
@@ -146,6 +174,293 @@ static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd)
        return ret;
 }
 
+static int ziirave_firm_wait_for_ack(struct watchdog_device *wdd)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       int ret;
+       unsigned long timeout;
+
+       timeout = jiffies + msecs_to_jiffies(ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT);
+       do {
+               if (time_after(jiffies, timeout))
+                       return -ETIMEDOUT;
+
+               usleep_range(5000, 10000);
+
+               ret = i2c_smbus_read_byte(client);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Failed to read byte\n");
+                       return ret;
+               }
+       } while (ret == ZIIRAVE_FIRM_DOWNLOAD_BUSY);
+
+       return ret == ZIIRAVE_FIRM_DOWNLOAD_ACK ? 0 : -EIO;
+}
+
+static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u16 addr)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       u8 address[2];
+
+       address[0] = addr & 0xff;
+       address[1] = (addr >> 8) & 0xff;
+
+       return i2c_smbus_write_block_data(client,
+                                         ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR,
+                                         ARRAY_SIZE(address), address);
+}
+
+static int ziirave_firm_write_block_data(struct watchdog_device *wdd,
+                                        u8 command, u8 length, const u8 *data,
+                                        bool wait_for_ack)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       int ret;
+
+       ret = i2c_smbus_write_block_data(client, command, length, data);
+       if (ret) {
+               dev_err(&client->dev,
+                       "Failed to send command 0x%02x: %d\n", command, ret);
+               return ret;
+       }
+
+       if (wait_for_ack)
+               ret = ziirave_firm_wait_for_ack(wdd);
+
+       return ret;
+}
+
+static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command,
+                                  u8 byte, bool wait_for_ack)
+{
+       return ziirave_firm_write_block_data(wdd, command, 1, &byte,
+                                            wait_for_ack);
+}
+
+/*
+ * ziirave_firm_write_pkt() - Build and write a firmware packet
+ *
+ * A packet to send to the firmware is composed by following bytes:
+ *     Length | Addr0 | Addr1 | Data0 .. Data15 | Checksum |
+ * Where,
+ *     Length: A data byte containing the length of the data.
+ *     Addr0: Low byte of the address.
+ *     Addr1: High byte of the address.
+ *     Data0 .. Data15: Array of 16 bytes of data.
+ *     Checksum: Checksum byte to verify data integrity.
+ */
+static int ziirave_firm_write_pkt(struct watchdog_device *wdd,
+                                 const struct ihex_binrec *rec)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       u8 i, checksum = 0, packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE];
+       int ret;
+       u16 addr;
+
+       memset(packet, 0, ARRAY_SIZE(packet));
+
+       /* Packet length */
+       packet[0] = (u8)be16_to_cpu(rec->len);
+       /* Packet address */
+       addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1;
+       packet[1] = addr & 0xff;
+       packet[2] = (addr & 0xff00) >> 8;
+
+       /* Packet data */
+       if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE)
+               return -EMSGSIZE;
+       memcpy(packet + 3, rec->data, be16_to_cpu(rec->len));
+
+       /* Packet checksum */
+       for (i = 0; i < ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1; i++)
+               checksum += packet[i];
+       packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1] = checksum;
+
+       ret = ziirave_firm_write_block_data(wdd, ZIIRAVE_CMD_DOWNLOAD_PACKET,
+                                           ARRAY_SIZE(packet), packet, true);
+       if (ret)
+               dev_err(&client->dev,
+                     "Failed to write firmware packet at address 0x%04x: %d\n",
+                     addr, ret);
+
+       return ret;
+}
+
+static int ziirave_firm_verify(struct watchdog_device *wdd,
+                              const struct firmware *fw)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       const struct ihex_binrec *rec;
+       int i, ret;
+       u8 data[ZIIRAVE_FIRM_PKT_DATA_SIZE];
+       u16 addr;
+
+       for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
+               /* Zero length marks end of records */
+               if (!be16_to_cpu(rec->len))
+                       break;
+
+               addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1;
+               if (addr < ZIIRAVE_FIRM_FLASH_MEMORY_START ||
+                   addr > ZIIRAVE_FIRM_FLASH_MEMORY_END)
+                       continue;
+
+               ret = ziirave_firm_set_read_addr(wdd, addr);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "Failed to send SET_READ_ADDR command: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               for (i = 0; i < ARRAY_SIZE(data); i++) {
+                       ret = i2c_smbus_read_byte_data(client,
+                                               ZIIRAVE_CMD_DOWNLOAD_READ_BYTE);
+                       if (ret < 0) {
+                               dev_err(&client->dev,
+                                       "Failed to READ DATA: %d\n", ret);
+                               return ret;
+                       }
+                       data[i] = ret;
+               }
+
+               if (memcmp(data, rec->data, be16_to_cpu(rec->len))) {
+                       dev_err(&client->dev,
+                               "Firmware mismatch at address 0x%04x\n", addr);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int ziirave_firm_upload(struct watchdog_device *wdd,
+                              const struct firmware *fw)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       int ret, words_till_page_break;
+       const struct ihex_binrec *rec;
+       struct ihex_binrec *rec_new;
+
+       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_JUMP_TO_BOOTLOADER, 1,
+                                     false);
+       if (ret)
+               return ret;
+
+       msleep(500);
+
+       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_START, 1, true);
+       if (ret)
+               return ret;
+
+       msleep(500);
+
+       for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
+               /* Zero length marks end of records */
+               if (!be16_to_cpu(rec->len))
+                       break;
+
+               /* Check max data size */
+               if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) {
+                       dev_err(&client->dev, "Firmware packet too long (%d)\n",
+                               be16_to_cpu(rec->len));
+                       return -EMSGSIZE;
+               }
+
+               /* Calculate words till page break */
+               words_till_page_break = (64 - ((be32_to_cpu(rec->addr) >> 1) &
+                                        0x3f));
+               if ((be16_to_cpu(rec->len) >> 1) > words_till_page_break) {
+                       /*
+                        * Data in passes page boundary, so we need to split in
+                        * two blocks of data. Create a packet with the first
+                        * block of data.
+                        */
+                       rec_new = kzalloc(sizeof(struct ihex_binrec) +
+                                         (words_till_page_break << 1),
+                                         GFP_KERNEL);
+                       if (!rec_new)
+                               return -ENOMEM;
+
+                       rec_new->len = cpu_to_be16(words_till_page_break << 1);
+                       rec_new->addr = rec->addr;
+                       memcpy(rec_new->data, rec->data,
+                              be16_to_cpu(rec_new->len));
+
+                       ret = ziirave_firm_write_pkt(wdd, rec_new);
+                       kfree(rec_new);
+                       if (ret)
+                               return ret;
+
+                       /* Create a packet with the second block of data */
+                       rec_new = kzalloc(sizeof(struct ihex_binrec) +
+                                         be16_to_cpu(rec->len) -
+                                         (words_till_page_break << 1),
+                                         GFP_KERNEL);
+                       if (!rec_new)
+                               return -ENOMEM;
+
+                       /* Remaining bytes */
+                       rec_new->len = rec->len -
+                                      cpu_to_be16(words_till_page_break << 1);
+
+                       rec_new->addr = cpu_to_be32(be32_to_cpu(rec->addr) +
+                                       (words_till_page_break << 1));
+
+                       memcpy(rec_new->data,
+                              rec->data + (words_till_page_break << 1),
+                              be16_to_cpu(rec_new->len));
+
+                       ret = ziirave_firm_write_pkt(wdd, rec_new);
+                       kfree(rec_new);
+                       if (ret)
+                               return ret;
+               } else {
+                       ret = ziirave_firm_write_pkt(wdd, rec);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       /* For end of download, the length field will be set to 0 */
+       rec_new = kzalloc(sizeof(struct ihex_binrec) + 1, GFP_KERNEL);
+       if (!rec_new)
+               return -ENOMEM;
+
+       ret = ziirave_firm_write_pkt(wdd, rec_new);
+       kfree(rec_new);
+       if (ret) {
+               dev_err(&client->dev, "Failed to send EMPTY packet: %d\n", ret);
+               return ret;
+       }
+
+       /* This sleep seems to be required */
+       msleep(20);
+
+       /* Start firmware verification */
+       ret = ziirave_firm_verify(wdd, fw);
+       if (ret) {
+               dev_err(&client->dev,
+                       "Failed to verify firmware: %d\n", ret);
+               return ret;
+       }
+
+       /* End download operation */
+       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_END, 1, false);
+       if (ret)
+               return ret;
+
+       /* Reset the processor */
+       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_RESET_PROCESSOR, 1,
+                                     false);
+       if (ret)
+               return ret;
+
+       msleep(500);
+
+       return 0;
+}
+
 static const struct watchdog_info ziirave_wdt_info = {
        .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
        .identity = "Zodiac RAVE Watchdog",
@@ -166,9 +481,18 @@ static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev,
 {
        struct i2c_client *client = to_i2c_client(dev->parent);
        struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
+       int ret;
+
+       ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
+       if (ret)
+               return ret;
+
+       ret = sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major,
+                     w_priv->firmware_rev.minor);
 
-       return sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major,
-                      w_priv->firmware_rev.minor);
+       mutex_unlock(&w_priv->sysfs_mutex);
+
+       return ret;
 }
 
 static DEVICE_ATTR(firmware_version, S_IRUGO, ziirave_wdt_sysfs_show_firm,
@@ -180,9 +504,18 @@ static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev,
 {
        struct i2c_client *client = to_i2c_client(dev->parent);
        struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
+       int ret;
 
-       return sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major,
-                      w_priv->bootloader_rev.minor);
+       ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
+       if (ret)
+               return ret;
+
+       ret = sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major,
+                     w_priv->bootloader_rev.minor);
+
+       mutex_unlock(&w_priv->sysfs_mutex);
+
+       return ret;
 }
 
 static DEVICE_ATTR(bootloader_version, S_IRUGO, ziirave_wdt_sysfs_show_boot,
@@ -194,17 +527,81 @@ static ssize_t ziirave_wdt_sysfs_show_reason(struct device *dev,
 {
        struct i2c_client *client = to_i2c_client(dev->parent);
        struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
+       int ret;
+
+       ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
+       if (ret)
+               return ret;
+
+       ret = sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]);
 
-       return sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]);
+       mutex_unlock(&w_priv->sysfs_mutex);
+
+       return ret;
 }
 
 static DEVICE_ATTR(reset_reason, S_IRUGO, ziirave_wdt_sysfs_show_reason,
                   NULL);
 
+static ssize_t ziirave_wdt_sysfs_store_firm(struct device *dev,
+                                           struct device_attribute *attr,
+                                           const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev->parent);
+       struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
+       const struct firmware *fw;
+       int err;
+
+       err = request_ihex_firmware(&fw, ZIIRAVE_FW_NAME, dev);
+       if (err) {
+               dev_err(&client->dev, "Failed to request ihex firmware\n");
+               return err;
+       }
+
+       err = mutex_lock_interruptible(&w_priv->sysfs_mutex);
+       if (err)
+               goto release_firmware;
+
+       err = ziirave_firm_upload(&w_priv->wdd, fw);
+       if (err) {
+               dev_err(&client->dev, "The firmware update failed: %d\n", err);
+               goto unlock_mutex;
+       }
+
+       /* Update firmware version */
+       err = ziirave_wdt_revision(client, &w_priv->firmware_rev,
+                                  ZIIRAVE_WDT_FIRM_VER_MAJOR);
+       if (err) {
+               dev_err(&client->dev, "Failed to read firmware version: %d\n",
+                       err);
+               goto unlock_mutex;
+       }
+
+       dev_info(&client->dev, "Firmware updated to version 02.%02u.%02u\n",
+                w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
+
+       /* Restore the watchdog timeout */
+       err = ziirave_wdt_set_timeout(&w_priv->wdd, w_priv->wdd.timeout);
+       if (err)
+               dev_err(&client->dev, "Failed to set timeout: %d\n", err);
+
+unlock_mutex:
+       mutex_unlock(&w_priv->sysfs_mutex);
+
+release_firmware:
+       release_firmware(fw);
+
+       return err ? err : count;
+}
+
+static DEVICE_ATTR(update_firmware, S_IWUSR, NULL,
+                  ziirave_wdt_sysfs_store_firm);
+
 static struct attribute *ziirave_wdt_attrs[] = {
        &dev_attr_firmware_version.attr,
        &dev_attr_bootloader_version.attr,
        &dev_attr_reset_reason.attr,
+       &dev_attr_update_firmware.attr,
        NULL
 };
 ATTRIBUTE_GROUPS(ziirave_wdt);
@@ -252,6 +649,8 @@ static int ziirave_wdt_probe(struct i2c_client *client,
        if (!w_priv)
                return -ENOMEM;
 
+       mutex_init(&w_priv->sysfs_mutex);
+
        w_priv->wdd.info = &ziirave_wdt_info;
        w_priv->wdd.ops = &ziirave_wdt_ops;
        w_priv->wdd.min_timeout = ZIIRAVE_TIMEOUT_MIN;
index e0f59263a96d5944c1a5315e17bf9ee872309b9f..c6bad51d8ec7b56d8027c019a98a656596911110 100644 (file)
@@ -43,7 +43,10 @@ struct befs_sb_info {
        u32 ag_shift;
        u32 num_ags;
 
-       /* jornal log entry */
+       /* State of the superblock */
+       u32 flags;
+
+       /* Journal log entry */
        befs_block_run log_blocks;
        befs_off_t log_start;
        befs_off_t log_end;
@@ -79,7 +82,7 @@ enum befs_err {
        BEFS_BT_END,
        BEFS_BT_EMPTY,
        BEFS_BT_MATCH,
-       BEFS_BT_PARMATCH,
+       BEFS_BT_OVERFLOW,
        BEFS_BT_NOT_FOUND
 };
 
@@ -140,18 +143,6 @@ befs_iaddrs_per_block(struct super_block *sb)
        return BEFS_SB(sb)->block_size / sizeof (befs_disk_inode_addr);
 }
 
-static inline int
-befs_iaddr_is_empty(const befs_inode_addr *iaddr)
-{
-       return (!iaddr->allocation_group) && (!iaddr->start) && (!iaddr->len);
-}
-
-static inline size_t
-befs_brun_size(struct super_block *sb, befs_block_run run)
-{
-       return BEFS_SB(sb)->block_size * run.len;
-}
-
 #include "endian.h"
 
 #endif                         /* _LINUX_BEFS_H */
index 307645f9e284c546db0e3d94e989e3064791b343..7e135ea73fddf65295a804502d7c3a57e906cffb 100644 (file)
@@ -85,7 +85,7 @@ struct befs_btree_node {
 };
 
 /* local constants */
-static const befs_off_t befs_bt_inval = 0xffffffffffffffffULL;
+static const befs_off_t BEFS_BT_INVAL = 0xffffffffffffffffULL;
 
 /* local functions */
 static int befs_btree_seekleaf(struct super_block *sb, const befs_data_stream *ds,
@@ -156,8 +156,6 @@ befs_bt_read_super(struct super_block *sb, const befs_data_stream *ds,
        sup->max_depth = fs32_to_cpu(sb, od_sup->max_depth);
        sup->data_type = fs32_to_cpu(sb, od_sup->data_type);
        sup->root_node_ptr = fs64_to_cpu(sb, od_sup->root_node_ptr);
-       sup->free_node_ptr = fs64_to_cpu(sb, od_sup->free_node_ptr);
-       sup->max_size = fs64_to_cpu(sb, od_sup->max_size);
 
        brelse(bh);
        if (sup->magic != BEFS_BTREE_MAGIC) {
@@ -183,8 +181,8 @@ befs_bt_read_super(struct super_block *sb, const befs_data_stream *ds,
  * Calls befs_read_datastream to read in the indicated btree node and
  * makes sure its header fields are in cpu byteorder, byteswapping if
  * necessary.
- * Note: node->bh must be NULL when this function called first
- * time. Don't forget brelse(node->bh) after last call.
+ * Note: node->bh must be NULL when this function is called the first time.
+ * Don't forget brelse(node->bh) after last call.
  *
  * On success, returns BEFS_OK and *@node contains the btree node that
  * starts at @node_off, with the node->head fields in cpu byte order.
@@ -244,7 +242,7 @@ befs_bt_read_node(struct super_block *sb, const befs_data_stream *ds,
  *   Read the superblock and rootnode of the b+tree.
  *   Drill down through the interior nodes using befs_find_key().
  *   Once at the correct leaf node, use befs_find_key() again to get the
- *   actuall value stored with the key.
+ *   actual value stored with the key.
  */
 int
 befs_btree_find(struct super_block *sb, const befs_data_stream *ds,
@@ -283,9 +281,9 @@ befs_btree_find(struct super_block *sb, const befs_data_stream *ds,
 
        while (!befs_leafnode(this_node)) {
                res = befs_find_key(sb, this_node, key, &node_off);
-               if (res == BEFS_BT_NOT_FOUND)
+               /* if no key set, try the overflow node */
+               if (res == BEFS_BT_OVERFLOW)
                        node_off = this_node->head.overflow;
-               /* if no match, go to overflow node */
                if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) {
                        befs_error(sb, "befs_btree_find() failed to read "
                                   "node at %llu", node_off);
@@ -293,15 +291,15 @@ befs_btree_find(struct super_block *sb, const befs_data_stream *ds,
                }
        }
 
-       /* at the correct leaf node now */
-
+       /* at a leaf node now, check if it is correct */
        res = befs_find_key(sb, this_node, key, value);
 
        brelse(this_node->bh);
        kfree(this_node);
 
        if (res != BEFS_BT_MATCH) {
-               befs_debug(sb, "<--- %s Key %s not found", __func__, key);
+               befs_error(sb, "<--- %s Key %s not found", __func__, key);
+               befs_debug(sb, "<--- %s ERROR", __func__);
                *value = 0;
                return BEFS_BT_NOT_FOUND;
        }
@@ -324,16 +322,12 @@ befs_btree_find(struct super_block *sb, const befs_data_stream *ds,
  * @findkey: Keystring to search for
  * @value: If key is found, the value stored with the key is put here
  *
- * finds exact match if one exists, and returns BEFS_BT_MATCH
- * If no exact match, finds first key in node that is greater
- * (alphabetically) than the search key and returns BEFS_BT_PARMATCH
- * (for partial match, I guess). Can you think of something better to
- * call it?
- *
- * If no key was a match or greater than the search key, return
- * BEFS_BT_NOT_FOUND.
+ * Finds exact match if one exists, and returns BEFS_BT_MATCH.
+ * If there is no match and node's value array is too small for key, return
+ * BEFS_BT_OVERFLOW.
+ * If no match and node should countain this key, return BEFS_BT_NOT_FOUND.
  *
- * Use binary search instead of a linear.
+ * Uses binary search instead of a linear.
  */
 static int
 befs_find_key(struct super_block *sb, struct befs_btree_node *node,
@@ -348,18 +342,16 @@ befs_find_key(struct super_block *sb, struct befs_btree_node *node,
 
        befs_debug(sb, "---> %s %s", __func__, findkey);
 
-       *value = 0;
-
        findkey_len = strlen(findkey);
 
-       /* if node can not contain key, just skeep this node */
+       /* if node can not contain key, just skip this node */
        last = node->head.all_key_count - 1;
        thiskey = befs_bt_get_key(sb, node, last, &keylen);
 
        eq = befs_compare_strings(thiskey, keylen, findkey, findkey_len);
        if (eq < 0) {
-               befs_debug(sb, "<--- %s %s not found", __func__, findkey);
-               return BEFS_BT_NOT_FOUND;
+               befs_debug(sb, "<--- node can't contain %s", findkey);
+               return BEFS_BT_OVERFLOW;
        }
 
        valarray = befs_bt_valarray(node);
@@ -387,12 +379,15 @@ befs_find_key(struct super_block *sb, struct befs_btree_node *node,
                else
                        first = mid + 1;
        }
+
+       /* return an existing value so caller can arrive to a leaf node */
        if (eq < 0)
                *value = fs64_to_cpu(sb, valarray[mid + 1]);
        else
                *value = fs64_to_cpu(sb, valarray[mid]);
-       befs_debug(sb, "<--- %s found %s at %d", __func__, thiskey, mid);
-       return BEFS_BT_PARMATCH;
+       befs_error(sb, "<--- %s %s not found", __func__, findkey);
+       befs_debug(sb, "<--- %s ERROR", __func__);
+       return BEFS_BT_NOT_FOUND;
 }
 
 /**
@@ -405,7 +400,7 @@ befs_find_key(struct super_block *sb, struct befs_btree_node *node,
  * @keysize: Length of the returned key
  * @value: Value stored with the returned key
  *
- * Heres how it works: Key_no is the index of the key/value pair to 
+ * Here's how it works: Key_no is the index of the key/value pair to
  * return in keybuf/value.
  * Bufsize is the size of keybuf (BEFS_NAME_LEN+1 is a good size). Keysize is 
  * the number of characters in the key (just a convenience).
@@ -422,7 +417,7 @@ befs_btree_read(struct super_block *sb, const befs_data_stream *ds,
 {
        struct befs_btree_node *this_node;
        befs_btree_super bt_super;
-       befs_off_t node_off = 0;
+       befs_off_t node_off;
        int cur_key;
        fs64 *valarray;
        char *keystart;
@@ -467,7 +462,7 @@ befs_btree_read(struct super_block *sb, const befs_data_stream *ds,
        while (key_sum + this_node->head.all_key_count <= key_no) {
 
                /* no more nodes to look in: key_no is too large */
-               if (this_node->head.right == befs_bt_inval) {
+               if (this_node->head.right == BEFS_BT_INVAL) {
                        *keysize = 0;
                        *value = 0;
                        befs_debug(sb,
@@ -541,7 +536,6 @@ befs_btree_read(struct super_block *sb, const befs_data_stream *ds,
  * @node_off: Pointer to offset of current node within datastream. Modified
  *             by the function.
  *
- *
  * Helper function for btree traverse. Moves the current position to the 
  * start of the first leaf node.
  *
@@ -608,7 +602,7 @@ static int
 befs_leafnode(struct befs_btree_node *node)
 {
        /* all interior nodes (and only interior nodes) have an overflow node */
-       if (node->head.overflow == befs_bt_inval)
+       if (node->head.overflow == BEFS_BT_INVAL)
                return 1;
        else
                return 0;
@@ -715,7 +709,7 @@ befs_bt_get_key(struct super_block *sb, struct befs_btree_node *node,
  *
  * Returns 0 if @key1 and @key2 are equal.
  * Returns >0 if @key1 is greater.
- * Returns <0 if @key2 is greater..
+ * Returns <0 if @key2 is greater.
  */
 static int
 befs_compare_strings(const void *key1, int keylen1,
index af1bc19b7c85b582e5c87981b92beb65deeefcf5..b4c7ba013c0d6e752296599d6b29597e5dd13512 100644 (file)
@@ -22,22 +22,22 @@ const befs_inode_addr BAD_IADDR = { 0, 0, 0 };
 
 static int befs_find_brun_direct(struct super_block *sb,
                                 const befs_data_stream *data,
-                                befs_blocknr_t blockno, befs_block_run * run);
+                                befs_blocknr_t blockno, befs_block_run *run);
 
 static int befs_find_brun_indirect(struct super_block *sb,
                                   const befs_data_stream *data,
                                   befs_blocknr_t blockno,
-                                  befs_block_run * run);
+                                  befs_block_run *run);
 
 static int befs_find_brun_dblindirect(struct super_block *sb,
                                      const befs_data_stream *data,
                                      befs_blocknr_t blockno,
-                                     befs_block_run * run);
+                                     befs_block_run *run);
 
 /**
  * befs_read_datastream - get buffer_head containing data, starting from pos.
  * @sb: Filesystem superblock
- * @ds: datastrem to find data with
+ * @ds: datastream to find data with
  * @pos: start of data
  * @off: offset of data in buffer_head->b_data
  *
@@ -46,7 +46,7 @@ static int befs_find_brun_dblindirect(struct super_block *sb,
  */
 struct buffer_head *
 befs_read_datastream(struct super_block *sb, const befs_data_stream *ds,
-                    befs_off_t pos, uint * off)
+                    befs_off_t pos, uint *off)
 {
        struct buffer_head *bh;
        befs_block_run run;
@@ -75,7 +75,13 @@ befs_read_datastream(struct super_block *sb, const befs_data_stream *ds,
        return bh;
 }
 
-/*
+/**
+ * befs_fblock2brun - give back block run for fblock
+ * @sb: the superblock
+ * @data: datastream to read from
+ * @fblock: the blocknumber with the file position to find
+ * @run: The found run is passed back through this pointer
+ *
  * Takes a file position and gives back a brun who's starting block
  * is block number fblock of the file.
  * 
@@ -88,7 +94,7 @@ befs_read_datastream(struct super_block *sb, const befs_data_stream *ds,
  */
 int
 befs_fblock2brun(struct super_block *sb, const befs_data_stream *data,
-                befs_blocknr_t fblock, befs_block_run * run)
+                befs_blocknr_t fblock, befs_block_run *run)
 {
        int err;
        befs_off_t pos = fblock << BEFS_SB(sb)->block_shift;
@@ -115,7 +121,7 @@ befs_fblock2brun(struct super_block *sb, const befs_data_stream *data,
 /**
  * befs_read_lsmylink - read long symlink from datastream.
  * @sb: Filesystem superblock 
- * @ds: Datastrem to read from
+ * @ds: Datastream to read from
  * @buff: Buffer in which to place long symlink data
  * @len: Length of the long symlink in bytes
  *
@@ -128,6 +134,7 @@ befs_read_lsymlink(struct super_block *sb, const befs_data_stream *ds,
        befs_off_t bytes_read = 0;      /* bytes readed */
        u16 plen;
        struct buffer_head *bh;
+
        befs_debug(sb, "---> %s length: %llu", __func__, len);
 
        while (bytes_read < len) {
@@ -183,13 +190,13 @@ befs_count_blocks(struct super_block *sb, const befs_data_stream *ds)
                metablocks += ds->indirect.len;
 
        /*
-          Double indir block, plus all the indirect blocks it mapps
-          In the double-indirect range, all block runs of data are
-          BEFS_DBLINDIR_BRUN_LEN blocks long. Therefore, we know 
-          how many data block runs are in the double-indirect region,
-          and from that we know how many indirect blocks it takes to
-          map them. We assume that the indirect blocks are also
-          BEFS_DBLINDIR_BRUN_LEN blocks long.
+        * Double indir block, plus all the indirect blocks it maps.
+        * In the double-indirect range, all block runs of data are
+        * BEFS_DBLINDIR_BRUN_LEN blocks long. Therefore, we know
+        * how many data block runs are in the double-indirect region,
+        * and from that we know how many indirect blocks it takes to
+        * map them. We assume that the indirect blocks are also
+        * BEFS_DBLINDIR_BRUN_LEN blocks long.
         */
        if (ds->size > ds->max_indirect_range && ds->max_indirect_range != 0) {
                uint dbl_bytes;
@@ -212,58 +219,50 @@ befs_count_blocks(struct super_block *sb, const befs_data_stream *ds)
        return blocks;
 }
 
-/*
-       Finds the block run that starts at file block number blockno
-       in the file represented by the datastream data, if that 
-       blockno is in the direct region of the datastream.
-       
-       sb: the superblock
-       data: the datastream
-       blockno: the blocknumber to find
-       run: The found run is passed back through this pointer
-       
-       Return value is BEFS_OK if the blockrun is found, BEFS_ERR
-       otherwise.
-       
-       Algorithm:
-       Linear search. Checks each element of array[] to see if it
-       contains the blockno-th filesystem block. This is necessary
-       because the block runs map variable amounts of data. Simply
-       keeps a count of the number of blocks searched so far (sum),
-       incrementing this by the length of each block run as we come
-       across it. Adds sum to *count before returning (this is so
-       you can search multiple arrays that are logicaly one array,
-       as in the indirect region code).
-       
-       When/if blockno is found, if blockno is inside of a block 
-       run as stored on disk, we offset the start and length members
-       of the block run, so that blockno is the start and len is
-       still valid (the run ends in the same place).
-       
-       2001-11-15 Will Dyson
-*/
+/**
+ * befs_find_brun_direct - find a direct block run in the datastream
+ * @sb: the superblock
+ * @data: the datastream
+ * @blockno: the blocknumber to find
+ * @run: The found run is passed back through this pointer
+ *
+ * Finds the block run that starts at file block number blockno
+ * in the file represented by the datastream data, if that
+ * blockno is in the direct region of the datastream.
+ *
+ * Return value is BEFS_OK if the blockrun is found, BEFS_ERR
+ * otherwise.
+ *
+ * Algorithm:
+ * Linear search. Checks each element of array[] to see if it
+ * contains the blockno-th filesystem block. This is necessary
+ * because the block runs map variable amounts of data. Simply
+ * keeps a count of the number of blocks searched so far (sum),
+ * incrementing this by the length of each block run as we come
+ * across it. Adds sum to *count before returning (this is so
+ * you can search multiple arrays that are logicaly one array,
+ * as in the indirect region code).
+ *
+ * When/if blockno is found, if blockno is inside of a block
+ * run as stored on disk, we offset the start and length members
+ * of the block run, so that blockno is the start and len is
+ * still valid (the run ends in the same place).
+ */
 static int
 befs_find_brun_direct(struct super_block *sb, const befs_data_stream *data,
-                     befs_blocknr_t blockno, befs_block_run * run)
+                     befs_blocknr_t blockno, befs_block_run *run)
 {
        int i;
        const befs_block_run *array = data->direct;
        befs_blocknr_t sum;
-       befs_blocknr_t max_block =
-           data->max_direct_range >> BEFS_SB(sb)->block_shift;
 
        befs_debug(sb, "---> %s, find %lu", __func__, (unsigned long)blockno);
 
-       if (blockno > max_block) {
-               befs_error(sb, "%s passed block outside of direct region",
-                          __func__);
-               return BEFS_ERR;
-       }
-
        for (i = 0, sum = 0; i < BEFS_NUM_DIRECT_BLOCKS;
             sum += array[i].len, i++) {
                if (blockno >= sum && blockno < sum + (array[i].len)) {
                        int offset = blockno - sum;
+
                        run->allocation_group = array[i].allocation_group;
                        run->start = array[i].start + offset;
                        run->len = array[i].len - offset;
@@ -275,38 +274,39 @@ befs_find_brun_direct(struct super_block *sb, const befs_data_stream *data,
                }
        }
 
+       befs_error(sb, "%s failed to find file block %lu", __func__,
+                  (unsigned long)blockno);
        befs_debug(sb, "---> %s ERROR", __func__);
        return BEFS_ERR;
 }
 
-/*
-       Finds the block run that starts at file block number blockno
-       in the file represented by the datastream data, if that 
-       blockno is in the indirect region of the datastream.
-       
-       sb: the superblock
-       data: the datastream
-       blockno: the blocknumber to find
-       run: The found run is passed back through this pointer
-       
-       Return value is BEFS_OK if the blockrun is found, BEFS_ERR
-       otherwise.
-       
-       Algorithm:
-       For each block in the indirect run of the datastream, read
-       it in and search through it for search_blk.
-       
-       XXX:
-       Really should check to make sure blockno is inside indirect
-       region.
-       
-       2001-11-15 Will Dyson
-*/
+/**
+ * befs_find_brun_indirect - find a block run in the datastream
+ * @sb: the superblock
+ * @data: the datastream
+ * @blockno: the blocknumber to find
+ * @run: The found run is passed back through this pointer
+ *
+ * Finds the block run that starts at file block number blockno
+ * in the file represented by the datastream data, if that
+ * blockno is in the indirect region of the datastream.
+ *
+ * Return value is BEFS_OK if the blockrun is found, BEFS_ERR
+ * otherwise.
+ *
+ * Algorithm:
+ * For each block in the indirect run of the datastream, read
+ * it in and search through it for search_blk.
+ *
+ * XXX:
+ * Really should check to make sure blockno is inside indirect
+ * region.
+ */
 static int
 befs_find_brun_indirect(struct super_block *sb,
                        const befs_data_stream *data,
                        befs_blocknr_t blockno,
-                       befs_block_run * run)
+                       befs_block_run *run)
 {
        int i, j;
        befs_blocknr_t sum = 0;
@@ -326,11 +326,12 @@ befs_find_brun_indirect(struct super_block *sb,
 
        /* Examine blocks of the indirect run one at a time */
        for (i = 0; i < indirect.len; i++) {
-               indirblock = befs_bread(sb, indirblockno + i);
+               indirblock = sb_bread(sb, indirblockno + i);
                if (indirblock == NULL) {
-                       befs_debug(sb, "---> %s failed to read "
+                       befs_error(sb, "---> %s failed to read "
                                   "disk block %lu from the indirect brun",
                                   __func__, (unsigned long)indirblockno + i);
+                       befs_debug(sb, "<--- %s ERROR", __func__);
                        return BEFS_ERR;
                }
 
@@ -370,52 +371,51 @@ befs_find_brun_indirect(struct super_block *sb,
        return BEFS_ERR;
 }
 
-/*
-       Finds the block run that starts at file block number blockno
-       in the file represented by the datastream data, if that 
-       blockno is in the double-indirect region of the datastream.
-       
-       sb: the superblock
-       data: the datastream
-       blockno: the blocknumber to find
-       run: The found run is passed back through this pointer
-       
-       Return value is BEFS_OK if the blockrun is found, BEFS_ERR
-       otherwise.
-       
-       Algorithm:
-       The block runs in the double-indirect region are different.
-       They are always allocated 4 fs blocks at a time, so each
-       block run maps a constant amount of file data. This means
-       that we can directly calculate how many block runs into the
-       double-indirect region we need to go to get to the one that
-       maps a particular filesystem block.
-       
-       We do this in two stages. First we calculate which of the
-       inode addresses in the double-indirect block will point us
-       to the indirect block that contains the mapping for the data,
-       then we calculate which of the inode addresses in that 
-       indirect block maps the data block we are after.
-       
-       Oh, and once we've done that, we actually read in the blocks 
-       that contain the inode addresses we calculated above. Even 
-       though the double-indirect run may be several blocks long, 
-       we can calculate which of those blocks will contain the index
-       we are after and only read that one. We then follow it to 
-       the indirect block and perform a  similar process to find
-       the actual block run that maps the data block we are interested
-       in.
-       
-       Then we offset the run as in befs_find_brun_array() and we are 
-       done.
-       
-       2001-11-15 Will Dyson
-*/
+/**
+ * befs_find_brun_dblindirect - find a block run in the datastream
+ * @sb: the superblock
+ * @data: the datastream
+ * @blockno: the blocknumber to find
+ * @run: The found run is passed back through this pointer
+ *
+ * Finds the block run that starts at file block number blockno
+ * in the file represented by the datastream data, if that
+ * blockno is in the double-indirect region of the datastream.
+ *
+ * Return value is BEFS_OK if the blockrun is found, BEFS_ERR
+ * otherwise.
+ *
+ * Algorithm:
+ * The block runs in the double-indirect region are different.
+ * They are always allocated 4 fs blocks at a time, so each
+ * block run maps a constant amount of file data. This means
+ * that we can directly calculate how many block runs into the
+ * double-indirect region we need to go to get to the one that
+ * maps a particular filesystem block.
+ *
+ * We do this in two stages. First we calculate which of the
+ * inode addresses in the double-indirect block will point us
+ * to the indirect block that contains the mapping for the data,
+ * then we calculate which of the inode addresses in that
+ * indirect block maps the data block we are after.
+ *
+ * Oh, and once we've done that, we actually read in the blocks
+ * that contain the inode addresses we calculated above. Even
+ * though the double-indirect run may be several blocks long,
+ * we can calculate which of those blocks will contain the index
+ * we are after and only read that one. We then follow it to
+ * the indirect block and perform a similar process to find
+ * the actual block run that maps the data block we are interested
+ * in.
+ *
+ * Then we offset the run as in befs_find_brun_array() and we are
+ * done.
+ */
 static int
 befs_find_brun_dblindirect(struct super_block *sb,
                           const befs_data_stream *data,
                           befs_blocknr_t blockno,
-                          befs_block_run * run)
+                          befs_block_run *run)
 {
        int dblindir_indx;
        int indir_indx;
@@ -430,10 +430,9 @@ befs_find_brun_dblindirect(struct super_block *sb,
        struct buffer_head *indir_block;
        befs_block_run indir_run;
        befs_disk_inode_addr *iaddr_array;
-       struct befs_sb_info *befs_sb = BEFS_SB(sb);
 
        befs_blocknr_t indir_start_blk =
-           data->max_indirect_range >> befs_sb->block_shift;
+           data->max_indirect_range >> BEFS_SB(sb)->block_shift;
 
        off_t dbl_indir_off = blockno - indir_start_blk;
 
@@ -471,7 +470,7 @@ befs_find_brun_dblindirect(struct super_block *sb,
        }
 
        dbl_indir_block =
-           befs_bread(sb, iaddr2blockno(sb, &data->double_indirect) +
+           sb_bread(sb, iaddr2blockno(sb, &data->double_indirect) +
                                        dbl_which_block);
        if (dbl_indir_block == NULL) {
                befs_error(sb, "%s couldn't read the "
@@ -479,7 +478,6 @@ befs_find_brun_dblindirect(struct super_block *sb,
                           (unsigned long)
                           iaddr2blockno(sb, &data->double_indirect) +
                           dbl_which_block);
-               brelse(dbl_indir_block);
                return BEFS_ERR;
        }
 
@@ -499,12 +497,11 @@ befs_find_brun_dblindirect(struct super_block *sb,
        }
 
        indir_block =
-           befs_bread(sb, iaddr2blockno(sb, &indir_run) + which_block);
+           sb_bread(sb, iaddr2blockno(sb, &indir_run) + which_block);
        if (indir_block == NULL) {
                befs_error(sb, "%s couldn't read the indirect block "
                           "at blockno %lu", __func__, (unsigned long)
                           iaddr2blockno(sb, &indir_run) + which_block);
-               brelse(indir_block);
                return BEFS_ERR;
        }
 
index 4de7cffcd66223c4a85a216cb58e5a8e62953e6c..85c13392e9e897dae2ee288f301c88c3067e8303 100644 (file)
@@ -169,6 +169,7 @@ befs_dump_super_block(const struct super_block *sb, befs_super_block * sup)
 
        befs_debug(sb, "  num_blocks %llu", fs64_to_cpu(sb, sup->num_blocks));
        befs_debug(sb, "  used_blocks %llu", fs64_to_cpu(sb, sup->used_blocks));
+       befs_debug(sb, "  inode_size %u", fs32_to_cpu(sb, sup->inode_size));
 
        befs_debug(sb, "  magic2 %08x", fs32_to_cpu(sb, sup->magic2));
        befs_debug(sb, "  blocks_per_ag %u",
index 523c8af2d770b08473bfa6200d635ae46364b0e2..b4a558126ee1724b0d3bd833f68a1c201833af33 100644 (file)
@@ -27,7 +27,7 @@ struct buffer_head *
 befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr)
 {
        struct buffer_head *bh;
-       befs_blocknr_t block = 0;
+       befs_blocknr_t block;
        struct befs_sb_info *befs_sb = BEFS_SB(sb);
 
        befs_debug(sb, "---> Enter %s "
@@ -59,27 +59,3 @@ befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr)
        befs_debug(sb, "<--- %s ERROR", __func__);
        return NULL;
 }
-
-struct buffer_head *
-befs_bread(struct super_block *sb, befs_blocknr_t block)
-{
-       struct buffer_head *bh;
-
-       befs_debug(sb, "---> Enter %s %lu", __func__, (unsigned long)block);
-
-       bh = sb_bread(sb, block);
-
-       if (bh == NULL) {
-               befs_error(sb, "Failed to read block %lu",
-                          (unsigned long)block);
-               goto error;
-       }
-
-       befs_debug(sb, "<--- %s", __func__);
-
-       return bh;
-
-      error:
-       befs_debug(sb, "<--- %s ERROR", __func__);
-       return NULL;
-}
index 9b78266b6aa57b73637167d0fb426ccc08be2cff..78d7bc6e60dee4d51fb1ac6325cca8b3929bd9c2 100644 (file)
@@ -5,5 +5,3 @@
 struct buffer_head *befs_bread_iaddr(struct super_block *sb,
                                     befs_inode_addr iaddr);
 
-struct buffer_head *befs_bread(struct super_block *sb, befs_blocknr_t block);
-
index bfe9f999493531b3bbbc348e053f6f581db878fe..647a276eba5654593739aafa9a6984ca967d8adf 100644 (file)
@@ -120,7 +120,7 @@ befs_get_block(struct inode *inode, sector_t block,
        struct super_block *sb = inode->i_sb;
        befs_data_stream *ds = &BEFS_I(inode)->i_data.ds;
        befs_block_run run = BAD_IADDR;
-       int res = 0;
+       int res;
        ulong disk_off;
 
        befs_debug(sb, "---> befs_get_block() for inode %lu, block %ld",
@@ -179,15 +179,16 @@ befs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
                kfree(utfname);
 
        } else {
-               ret = befs_btree_find(sb, ds, dentry->d_name.name, &offset);
+               ret = befs_btree_find(sb, ds, name, &offset);
        }
 
        if (ret == BEFS_BT_NOT_FOUND) {
                befs_debug(sb, "<--- %s %pd not found", __func__, dentry);
+               d_add(dentry, NULL);
                return ERR_PTR(-ENOENT);
 
        } else if (ret != BEFS_OK || offset == 0) {
-               befs_warning(sb, "<--- %s Error", __func__);
+               befs_error(sb, "<--- %s Error", __func__);
                return ERR_PTR(-ENODATA);
        }
 
@@ -211,56 +212,55 @@ befs_readdir(struct file *file, struct dir_context *ctx)
        befs_off_t value;
        int result;
        size_t keysize;
-       unsigned char d_type;
        char keybuf[BEFS_NAME_LEN + 1];
 
        befs_debug(sb, "---> %s name %pD, inode %ld, ctx->pos %lld",
                  __func__, file, inode->i_ino, ctx->pos);
 
-more:
-       result = befs_btree_read(sb, ds, ctx->pos, BEFS_NAME_LEN + 1,
-                                keybuf, &keysize, &value);
+       while (1) {
+               result = befs_btree_read(sb, ds, ctx->pos, BEFS_NAME_LEN + 1,
+                                        keybuf, &keysize, &value);
 
-       if (result == BEFS_ERR) {
-               befs_debug(sb, "<--- %s ERROR", __func__);
-               befs_error(sb, "IO error reading %pD (inode %lu)",
-                          file, inode->i_ino);
-               return -EIO;
-
-       } else if (result == BEFS_BT_END) {
-               befs_debug(sb, "<--- %s END", __func__);
-               return 0;
-
-       } else if (result == BEFS_BT_EMPTY) {
-               befs_debug(sb, "<--- %s Empty directory", __func__);
-               return 0;
-       }
+               if (result == BEFS_ERR) {
+                       befs_debug(sb, "<--- %s ERROR", __func__);
+                       befs_error(sb, "IO error reading %pD (inode %lu)",
+                                  file, inode->i_ino);
+                       return -EIO;
 
-       d_type = DT_UNKNOWN;
+               } else if (result == BEFS_BT_END) {
+                       befs_debug(sb, "<--- %s END", __func__);
+                       return 0;
 
-       /* Convert to NLS */
-       if (BEFS_SB(sb)->nls) {
-               char *nlsname;
-               int nlsnamelen;
-               result =
-                   befs_utf2nls(sb, keybuf, keysize, &nlsname, &nlsnamelen);
-               if (result < 0) {
-                       befs_debug(sb, "<--- %s ERROR", __func__);
-                       return result;
+               } else if (result == BEFS_BT_EMPTY) {
+                       befs_debug(sb, "<--- %s Empty directory", __func__);
+                       return 0;
                }
-               if (!dir_emit(ctx, nlsname, nlsnamelen,
-                                (ino_t) value, d_type)) {
+
+               /* Convert to NLS */
+               if (BEFS_SB(sb)->nls) {
+                       char *nlsname;
+                       int nlsnamelen;
+
+                       result =
+                           befs_utf2nls(sb, keybuf, keysize, &nlsname,
+                                        &nlsnamelen);
+                       if (result < 0) {
+                               befs_debug(sb, "<--- %s ERROR", __func__);
+                               return result;
+                       }
+                       if (!dir_emit(ctx, nlsname, nlsnamelen,
+                                     (ino_t) value, DT_UNKNOWN)) {
+                               kfree(nlsname);
+                               return 0;
+                       }
                        kfree(nlsname);
-                       return 0;
+               } else {
+                       if (!dir_emit(ctx, keybuf, keysize,
+                                     (ino_t) value, DT_UNKNOWN))
+                               return 0;
                }
-               kfree(nlsname);
-       } else {
-               if (!dir_emit(ctx, keybuf, keysize,
-                                (ino_t) value, d_type))
-                       return 0;
+               ctx->pos++;
        }
-       ctx->pos++;
-       goto more;
 }
 
 static struct inode *
@@ -299,7 +299,6 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
        struct befs_sb_info *befs_sb = BEFS_SB(sb);
        struct befs_inode_info *befs_ino;
        struct inode *inode;
-       long ret = -EIO;
 
        befs_debug(sb, "---> %s inode = %lu", __func__, ino);
 
@@ -318,7 +317,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
                   befs_ino->i_inode_num.allocation_group,
                   befs_ino->i_inode_num.start, befs_ino->i_inode_num.len);
 
-       bh = befs_bread(sb, inode->i_ino);
+       bh = sb_bread(sb, inode->i_ino);
        if (!bh) {
                befs_error(sb, "unable to read inode block - "
                           "inode = %lu", inode->i_ino);
@@ -421,7 +420,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
       unacquire_none:
        iget_failed(inode);
        befs_debug(sb, "<--- %s - Bad inode", __func__);
-       return ERR_PTR(ret);
+       return ERR_PTR(-EIO);
 }
 
 /* Initialize the inode cache. Called at fs setup.
@@ -436,10 +435,9 @@ befs_init_inodecache(void)
                                              0, (SLAB_RECLAIM_ACCOUNT|
                                                SLAB_MEM_SPREAD|SLAB_ACCOUNT),
                                              init_once);
-       if (befs_inode_cachep == NULL) {
-               pr_err("%s: Couldn't initialize inode slabcache\n", __func__);
+       if (befs_inode_cachep == NULL)
                return -ENOMEM;
-       }
+
        return 0;
 }
 
@@ -524,8 +522,6 @@ befs_utf2nls(struct super_block *sb, const char *in,
 
        *out = result = kmalloc(maxlen, GFP_NOFS);
        if (!*out) {
-               befs_error(sb, "%s cannot allocate memory", __func__);
-               *out_len = 0;
                return -ENOMEM;
        }
 
@@ -604,7 +600,6 @@ befs_nls2utf(struct super_block *sb, const char *in,
 
        *out = result = kmalloc(maxlen, GFP_NOFS);
        if (!*out) {
-               befs_error(sb, "%s cannot allocate memory", __func__);
                *out_len = 0;
                return -ENOMEM;
        }
@@ -637,10 +632,6 @@ befs_nls2utf(struct super_block *sb, const char *in,
        return -EILSEQ;
 }
 
-/**
- * Use the
- *
- */
 enum {
        Opt_uid, Opt_gid, Opt_charset, Opt_debug, Opt_err,
 };
@@ -760,19 +751,19 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
        long ret = -EINVAL;
        const unsigned long sb_block = 0;
        const off_t x86_sb_off = 512;
+       int blocksize;
 
        save_mount_options(sb, data);
 
        sb->s_fs_info = kzalloc(sizeof(*befs_sb), GFP_KERNEL);
-       if (sb->s_fs_info == NULL) {
-               pr_err("(%s): Unable to allocate memory for private "
-                      "portion of superblock. Bailing.\n", sb->s_id);
+       if (sb->s_fs_info == NULL)
                goto unacquire_none;
-       }
+
        befs_sb = BEFS_SB(sb);
 
        if (!parse_options((char *) data, &befs_sb->mount_opts)) {
-               befs_error(sb, "cannot parse mount options");
+               if (!silent)
+                       befs_error(sb, "cannot parse mount options");
                goto unacquire_priv_sbp;
        }
 
@@ -793,10 +784,16 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
         * least 1k to get the second 512 bytes of the volume.
         * -WD 10-26-01
         */ 
-       sb_min_blocksize(sb, 1024);
+       blocksize = sb_min_blocksize(sb, 1024);
+       if (!blocksize) {
+               if (!silent)
+                       befs_error(sb, "unable to set blocksize");
+               goto unacquire_priv_sbp;
+       }
 
        if (!(bh = sb_bread(sb, sb_block))) {
-               befs_error(sb, "unable to read superblock");
+               if (!silent)
+                       befs_error(sb, "unable to read superblock");
                goto unacquire_priv_sbp;
        }
 
@@ -820,9 +817,9 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
        brelse(bh);
 
        if( befs_sb->num_blocks > ~((sector_t)0) ) {
-               befs_error(sb, "blocks count: %llu "
-                       "is larger than the host can use",
-                       befs_sb->num_blocks);
+               if (!silent)
+                       befs_error(sb, "blocks count: %llu is larger than the host can use",
+                                       befs_sb->num_blocks);
                goto unacquire_priv_sbp;
        }
 
@@ -841,7 +838,8 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
        }
        sb->s_root = d_make_root(root);
        if (!sb->s_root) {
-               befs_error(sb, "get root inode failed");
+               if (!silent)
+                       befs_error(sb, "get root inode failed");
                goto unacquire_priv_sbp;
        }
 
@@ -870,9 +868,9 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
       unacquire_priv_sbp:
        kfree(befs_sb->mount_opts.iocharset);
        kfree(sb->s_fs_info);
+       sb->s_fs_info = NULL;
 
       unacquire_none:
-       sb->s_fs_info = NULL;
        return ret;
 }
 
index aeafc4d842788ca7cc760599b68e9d5988dd4110..7c50025c99d8ab38f2de8c3abe93a201143e0635 100644 (file)
 #include "befs.h"
 #include "super.h"
 
-/**
- * load_befs_sb -- Read from disk and properly byteswap all the fields
+/*
+ * befs_load_sb -- Read from disk and properly byteswap all the fields
  * of the befs superblock
- *
- *
- *
- *
  */
 int
-befs_load_sb(struct super_block *sb, befs_super_block * disk_sb)
+befs_load_sb(struct super_block *sb, befs_super_block *disk_sb)
 {
        struct befs_sb_info *befs_sb = BEFS_SB(sb);
 
        /* Check the byte order of the filesystem */
        if (disk_sb->fs_byte_order == BEFS_BYTEORDER_NATIVE_LE)
-           befs_sb->byte_order = BEFS_BYTESEX_LE;
+               befs_sb->byte_order = BEFS_BYTESEX_LE;
        else if (disk_sb->fs_byte_order == BEFS_BYTEORDER_NATIVE_BE)
-           befs_sb->byte_order = BEFS_BYTESEX_BE;
+               befs_sb->byte_order = BEFS_BYTESEX_BE;
 
        befs_sb->magic1 = fs32_to_cpu(sb, disk_sb->magic1);
        befs_sb->magic2 = fs32_to_cpu(sb, disk_sb->magic2);
@@ -45,6 +41,8 @@ befs_load_sb(struct super_block *sb, befs_super_block * disk_sb)
        befs_sb->ag_shift = fs32_to_cpu(sb, disk_sb->ag_shift);
        befs_sb->num_ags = fs32_to_cpu(sb, disk_sb->num_ags);
 
+       befs_sb->flags = fs32_to_cpu(sb, disk_sb->flags);
+
        befs_sb->log_blocks = fsrun_to_cpu(sb, disk_sb->log_blocks);
        befs_sb->log_start = fs64_to_cpu(sb, disk_sb->log_start);
        befs_sb->log_end = fs64_to_cpu(sb, disk_sb->log_end);
@@ -84,15 +82,15 @@ befs_check_sb(struct super_block *sb)
        }
 
        if (befs_sb->block_size > PAGE_SIZE) {
-               befs_error(sb, "blocksize(%u) cannot be larger"
+               befs_error(sb, "blocksize(%u) cannot be larger "
                           "than system pagesize(%lu)", befs_sb->block_size,
                           PAGE_SIZE);
                return BEFS_ERR;
        }
 
        /*
-          * block_shift and block_size encode the same information
-          * in different ways as a consistency check.
+        * block_shift and block_size encode the same information
+        * in different ways as a consistency check.
         */
 
        if ((1 << befs_sb->block_shift) != befs_sb->block_size) {
@@ -101,10 +99,18 @@ befs_check_sb(struct super_block *sb)
                return BEFS_ERR;
        }
 
-       if (befs_sb->log_start != befs_sb->log_end) {
+
+       /* ag_shift also encodes the same information as blocks_per_ag in a
+        * different way, non-fatal consistency check
+        */
+       if ((1 << befs_sb->ag_shift) != befs_sb->blocks_per_ag)
+               befs_error(sb, "ag_shift disagrees with blocks_per_ag.");
+
+       if (befs_sb->log_start != befs_sb->log_end ||
+           befs_sb->flags == BEFS_DIRTY) {
                befs_error(sb, "Filesystem not clean! There are blocks in the "
-                          "journal. You must boot into BeOS and mount this volume "
-                          "to make it clean.");
+                          "journal. You must boot into BeOS and mount this "
+                          "volume to make it clean.");
                return BEFS_ERR;
        }
 
index ccc70d96958d87e0b2db3fc901b513e51a9aa803..d4d8b7e36b2ffe7b84ddb78e4064b606aaee5e01 100644 (file)
@@ -698,7 +698,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
 
                        ret = btrfs_map_bio(root, comp_bio, mirror_num, 0);
                        if (ret) {
-                               bio->bi_error = ret;
+                               comp_bio->bi_error = ret;
                                bio_endio(comp_bio);
                        }
 
@@ -728,7 +728,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
 
        ret = btrfs_map_bio(root, comp_bio, mirror_num, 0);
        if (ret) {
-               bio->bi_error = ret;
+               comp_bio->bi_error = ret;
                bio_endio(comp_bio);
        }
 
index 6c21bad26a27ba909be4691d3a9ad640aa38cbb4..0b8ce2b9f7d0c8c052b8f73df269798450263784 100644 (file)
@@ -252,7 +252,8 @@ struct btrfs_super_block {
 #define BTRFS_FEATURE_COMPAT_SAFE_CLEAR                0ULL
 
 #define BTRFS_FEATURE_COMPAT_RO_SUPP                   \
-       (BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)
+       (BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE |      \
+        BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID)
 
 #define BTRFS_FEATURE_COMPAT_RO_SAFE_SET       0ULL
 #define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR     0ULL
index e720d3e6ec20e7179cb95ae81f0fc3f76b3e1029..3a57f99d96aa7aa0af541e328af203ff73acdb64 100644 (file)
@@ -2586,6 +2586,7 @@ int open_ctree(struct super_block *sb,
        int num_backups_tried = 0;
        int backup_index = 0;
        int max_active;
+       int clear_free_space_tree = 0;
 
        tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info, GFP_KERNEL);
        chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info, GFP_KERNEL);
@@ -3148,6 +3149,26 @@ retry_root_backup:
        if (sb->s_flags & MS_RDONLY)
                return 0;
 
+       if (btrfs_test_opt(fs_info, CLEAR_CACHE) &&
+           btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
+               clear_free_space_tree = 1;
+       } else if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
+                  !btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID)) {
+               btrfs_warn(fs_info, "free space tree is invalid");
+               clear_free_space_tree = 1;
+       }
+
+       if (clear_free_space_tree) {
+               btrfs_info(fs_info, "clearing free space tree");
+               ret = btrfs_clear_free_space_tree(fs_info);
+               if (ret) {
+                       btrfs_warn(fs_info,
+                                  "failed to clear free space tree: %d", ret);
+                       close_ctree(tree_root);
+                       return ret;
+               }
+       }
+
        if (btrfs_test_opt(tree_root->fs_info, FREE_SPACE_TREE) &&
            !btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
                btrfs_info(fs_info, "creating free space tree");
@@ -3185,18 +3206,6 @@ retry_root_backup:
 
        btrfs_qgroup_rescan_resume(fs_info);
 
-       if (btrfs_test_opt(tree_root->fs_info, CLEAR_CACHE) &&
-           btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
-               btrfs_info(fs_info, "clearing free space tree");
-               ret = btrfs_clear_free_space_tree(fs_info);
-               if (ret) {
-                       btrfs_warn(fs_info,
-                               "failed to clear free space tree: %d", ret);
-                       close_ctree(tree_root);
-                       return ret;
-               }
-       }
-
        if (!fs_info->uuid_root) {
                btrfs_info(fs_info, "creating UUID tree");
                ret = btrfs_create_uuid_tree(fs_info);
index ee40384c394d5647a8f9ee65e8c28f0cd32426fc..66a755150056ed06af2c3bc636b4dc76f45adce5 100644 (file)
@@ -5558,17 +5558,45 @@ void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
        }
 }
 
-/*
- * The extent buffer bitmap operations are done with byte granularity because
- * bitmap items are not guaranteed to be aligned to a word and therefore a
- * single word in a bitmap may straddle two pages in the extent buffer.
- */
-#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
-#define BYTE_MASK ((1 << BITS_PER_BYTE) - 1)
-#define BITMAP_FIRST_BYTE_MASK(start) \
-       ((BYTE_MASK << ((start) & (BITS_PER_BYTE - 1))) & BYTE_MASK)
-#define BITMAP_LAST_BYTE_MASK(nbits) \
-       (BYTE_MASK >> (-(nbits) & (BITS_PER_BYTE - 1)))
+void le_bitmap_set(u8 *map, unsigned int start, int len)
+{
+       u8 *p = map + BIT_BYTE(start);
+       const unsigned int size = start + len;
+       int bits_to_set = BITS_PER_BYTE - (start % BITS_PER_BYTE);
+       u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(start);
+
+       while (len - bits_to_set >= 0) {
+               *p |= mask_to_set;
+               len -= bits_to_set;
+               bits_to_set = BITS_PER_BYTE;
+               mask_to_set = ~(u8)0;
+               p++;
+       }
+       if (len) {
+               mask_to_set &= BITMAP_LAST_BYTE_MASK(size);
+               *p |= mask_to_set;
+       }
+}
+
+void le_bitmap_clear(u8 *map, unsigned int start, int len)
+{
+       u8 *p = map + BIT_BYTE(start);
+       const unsigned int size = start + len;
+       int bits_to_clear = BITS_PER_BYTE - (start % BITS_PER_BYTE);
+       u8 mask_to_clear = BITMAP_FIRST_BYTE_MASK(start);
+
+       while (len - bits_to_clear >= 0) {
+               *p &= ~mask_to_clear;
+               len -= bits_to_clear;
+               bits_to_clear = BITS_PER_BYTE;
+               mask_to_clear = ~(u8)0;
+               p++;
+       }
+       if (len) {
+               mask_to_clear &= BITMAP_LAST_BYTE_MASK(size);
+               *p &= ~mask_to_clear;
+       }
+}
 
 /*
  * eb_bitmap_offset() - calculate the page and offset of the byte containing the
@@ -5612,7 +5640,7 @@ static inline void eb_bitmap_offset(struct extent_buffer *eb,
 int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
                           unsigned long nr)
 {
-       char *kaddr;
+       u8 *kaddr;
        struct page *page;
        unsigned long i;
        size_t offset;
@@ -5634,13 +5662,13 @@ int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
 void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
                              unsigned long pos, unsigned long len)
 {
-       char *kaddr;
+       u8 *kaddr;
        struct page *page;
        unsigned long i;
        size_t offset;
        const unsigned int size = pos + len;
        int bits_to_set = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
-       unsigned int mask_to_set = BITMAP_FIRST_BYTE_MASK(pos);
+       u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(pos);
 
        eb_bitmap_offset(eb, start, pos, &i, &offset);
        page = eb->pages[i];
@@ -5651,7 +5679,7 @@ void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
                kaddr[offset] |= mask_to_set;
                len -= bits_to_set;
                bits_to_set = BITS_PER_BYTE;
-               mask_to_set = ~0U;
+               mask_to_set = ~(u8)0;
                if (++offset >= PAGE_SIZE && len > 0) {
                        offset = 0;
                        page = eb->pages[++i];
@@ -5676,13 +5704,13 @@ void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
 void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
                                unsigned long pos, unsigned long len)
 {
-       char *kaddr;
+       u8 *kaddr;
        struct page *page;
        unsigned long i;
        size_t offset;
        const unsigned int size = pos + len;
        int bits_to_clear = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
-       unsigned int mask_to_clear = BITMAP_FIRST_BYTE_MASK(pos);
+       u8 mask_to_clear = BITMAP_FIRST_BYTE_MASK(pos);
 
        eb_bitmap_offset(eb, start, pos, &i, &offset);
        page = eb->pages[i];
@@ -5693,7 +5721,7 @@ void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
                kaddr[offset] &= ~mask_to_clear;
                len -= bits_to_clear;
                bits_to_clear = BITS_PER_BYTE;
-               mask_to_clear = ~0U;
+               mask_to_clear = ~(u8)0;
                if (++offset >= PAGE_SIZE && len > 0) {
                        offset = 0;
                        page = eb->pages[++i];
index 4a094f1dc7ef98cb283009bc73ad6c9927c21145..ab31d145227edf423e15b7fb72d2094f402f7e56 100644 (file)
  */
 #define EXTENT_PAGE_PRIVATE 1
 
+/*
+ * The extent buffer bitmap operations are done with byte granularity instead of
+ * word granularity for two reasons:
+ * 1. The bitmaps must be little-endian on disk.
+ * 2. Bitmap items are not guaranteed to be aligned to a word and therefore a
+ *    single word in a bitmap may straddle two pages in the extent buffer.
+ */
+#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
+#define BYTE_MASK ((1 << BITS_PER_BYTE) - 1)
+#define BITMAP_FIRST_BYTE_MASK(start) \
+       ((BYTE_MASK << ((start) & (BITS_PER_BYTE - 1))) & BYTE_MASK)
+#define BITMAP_LAST_BYTE_MASK(nbits) \
+       (BYTE_MASK >> (-(nbits) & (BITS_PER_BYTE - 1)))
+
+static inline int le_test_bit(int nr, const u8 *addr)
+{
+       return 1U & (addr[BIT_BYTE(nr)] >> (nr & (BITS_PER_BYTE-1)));
+}
+
+extern void le_bitmap_set(u8 *map, unsigned int start, int len);
+extern void le_bitmap_clear(u8 *map, unsigned int start, int len);
+
 struct extent_state;
 struct btrfs_root;
 struct btrfs_io_bio;
index e4a42a8e4f849bf87399bc9ebd03d6989ce2b868..57401b474ec6f1d0f050a003bb345c50f58c867f 100644 (file)
@@ -151,7 +151,7 @@ static inline u32 free_space_bitmap_size(u64 size, u32 sectorsize)
        return DIV_ROUND_UP((u32)div_u64(size, sectorsize), BITS_PER_BYTE);
 }
 
-static unsigned long *alloc_bitmap(u32 bitmap_size)
+static u8 *alloc_bitmap(u32 bitmap_size)
 {
        void *mem;
 
@@ -180,8 +180,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
        struct btrfs_free_space_info *info;
        struct btrfs_key key, found_key;
        struct extent_buffer *leaf;
-       unsigned long *bitmap;
-       char *bitmap_cursor;
+       u8 *bitmap, *bitmap_cursor;
        u64 start, end;
        u64 bitmap_range, i;
        u32 bitmap_size, flags, expected_extent_count;
@@ -231,7 +230,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
                                                block_group->sectorsize);
                                last = div_u64(found_key.objectid + found_key.offset - start,
                                               block_group->sectorsize);
-                               bitmap_set(bitmap, first, last - first);
+                               le_bitmap_set(bitmap, first, last - first);
 
                                extent_count++;
                                nr++;
@@ -270,7 +269,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
                goto out;
        }
 
-       bitmap_cursor = (char *)bitmap;
+       bitmap_cursor = bitmap;
        bitmap_range = block_group->sectorsize * BTRFS_FREE_SPACE_BITMAP_BITS;
        i = start;
        while (i < end) {
@@ -319,7 +318,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
        struct btrfs_free_space_info *info;
        struct btrfs_key key, found_key;
        struct extent_buffer *leaf;
-       unsigned long *bitmap;
+       u8 *bitmap;
        u64 start, end;
        /* Initialize to silence GCC. */
        u64 extent_start = 0;
@@ -363,7 +362,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
                                break;
                        } else if (found_key.type == BTRFS_FREE_SPACE_BITMAP_KEY) {
                                unsigned long ptr;
-                               char *bitmap_cursor;
+                               u8 *bitmap_cursor;
                                u32 bitmap_pos, data_size;
 
                                ASSERT(found_key.objectid >= start);
@@ -373,7 +372,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
                                bitmap_pos = div_u64(found_key.objectid - start,
                                                     block_group->sectorsize *
                                                     BITS_PER_BYTE);
-                               bitmap_cursor = ((char *)bitmap) + bitmap_pos;
+                               bitmap_cursor = bitmap + bitmap_pos;
                                data_size = free_space_bitmap_size(found_key.offset,
                                                                   block_group->sectorsize);
 
@@ -410,7 +409,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
        offset = start;
        bitnr = 0;
        while (offset < end) {
-               bit = !!test_bit(bitnr, bitmap);
+               bit = !!le_test_bit(bitnr, bitmap);
                if (prev_bit == 0 && bit == 1) {
                        extent_start = offset;
                } else if (prev_bit == 1 && bit == 0) {
@@ -1185,6 +1184,7 @@ int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info)
        }
 
        btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE);
+       btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID);
        clear_bit(BTRFS_FS_CREATING_FREE_SPACE_TREE, &fs_info->flags);
 
        ret = btrfs_commit_transaction(trans, tree_root);
@@ -1253,6 +1253,7 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
                return PTR_ERR(trans);
 
        btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE);
+       btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID);
        fs_info->free_space_root = NULL;
 
        ret = clear_free_space_tree(trans, free_space_root);
index d19ab0317283ca728963700c93cff334819eaf16..caad80bb9bd0116072af62a8aae96bac86db4c51 100644 (file)
@@ -273,20 +273,37 @@ out:
        return ret;
 }
 
-/**
- * test_bit_in_byte - Determine whether a bit is set in a byte
- * @nr: bit number to test
- * @addr: Address to start counting from
- */
-static inline int test_bit_in_byte(int nr, const u8 *addr)
+static int check_eb_bitmap(unsigned long *bitmap, struct extent_buffer *eb,
+                          unsigned long len)
 {
-       return 1UL & (addr[nr / BITS_PER_BYTE] >> (nr & (BITS_PER_BYTE - 1)));
+       unsigned long i;
+
+       for (i = 0; i < len * BITS_PER_BYTE; i++) {
+               int bit, bit1;
+
+               bit = !!test_bit(i, bitmap);
+               bit1 = !!extent_buffer_test_bit(eb, 0, i);
+               if (bit1 != bit) {
+                       test_msg("Bits do not match\n");
+                       return -EINVAL;
+               }
+
+               bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE,
+                                               i % BITS_PER_BYTE);
+               if (bit1 != bit) {
+                       test_msg("Offset bits do not match\n");
+                       return -EINVAL;
+               }
+       }
+       return 0;
 }
 
 static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
                             unsigned long len)
 {
-       unsigned long i, x;
+       unsigned long i, j;
+       u32 x;
+       int ret;
 
        memset(bitmap, 0, len);
        memset_extent_buffer(eb, 0, 0, len);
@@ -297,16 +314,18 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
 
        bitmap_set(bitmap, 0, len * BITS_PER_BYTE);
        extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE);
-       if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+       ret = check_eb_bitmap(bitmap, eb, len);
+       if (ret) {
                test_msg("Setting all bits failed\n");
-               return -EINVAL;
+               return ret;
        }
 
        bitmap_clear(bitmap, 0, len * BITS_PER_BYTE);
        extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE);
-       if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+       ret = check_eb_bitmap(bitmap, eb, len);
+       if (ret) {
                test_msg("Clearing all bits failed\n");
-               return -EINVAL;
+               return ret;
        }
 
        /* Straddling pages test */
@@ -316,9 +335,10 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
                        sizeof(long) * BITS_PER_BYTE);
                extent_buffer_bitmap_set(eb, PAGE_SIZE - sizeof(long) / 2, 0,
                                        sizeof(long) * BITS_PER_BYTE);
-               if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+               ret = check_eb_bitmap(bitmap, eb, len);
+               if (ret) {
                        test_msg("Setting straddling pages failed\n");
-                       return -EINVAL;
+                       return ret;
                }
 
                bitmap_set(bitmap, 0, len * BITS_PER_BYTE);
@@ -328,9 +348,10 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
                extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE);
                extent_buffer_bitmap_clear(eb, PAGE_SIZE - sizeof(long) / 2, 0,
                                        sizeof(long) * BITS_PER_BYTE);
-               if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+               ret = check_eb_bitmap(bitmap, eb, len);
+               if (ret) {
                        test_msg("Clearing straddling pages failed\n");
-                       return -EINVAL;
+                       return ret;
                }
        }
 
@@ -339,28 +360,22 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
         * something repetitive that could miss some hypothetical off-by-n bug.
         */
        x = 0;
-       for (i = 0; i < len / sizeof(long); i++) {
-               x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffUL;
-               bitmap[i] = x;
-       }
-       write_extent_buffer(eb, bitmap, 0, len);
-
-       for (i = 0; i < len * BITS_PER_BYTE; i++) {
-               int bit, bit1;
-
-               bit = !!test_bit_in_byte(i, (u8 *)bitmap);
-               bit1 = !!extent_buffer_test_bit(eb, 0, i);
-               if (bit1 != bit) {
-                       test_msg("Testing bit pattern failed\n");
-                       return -EINVAL;
+       bitmap_clear(bitmap, 0, len * BITS_PER_BYTE);
+       extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE);
+       for (i = 0; i < len * BITS_PER_BYTE / 32; i++) {
+               x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffU;
+               for (j = 0; j < 32; j++) {
+                       if (x & (1U << j)) {
+                               bitmap_set(bitmap, i * 32 + j, 1);
+                               extent_buffer_bitmap_set(eb, 0, i * 32 + j, 1);
+                       }
                }
+       }
 
-               bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE,
-                                               i % BITS_PER_BYTE);
-               if (bit1 != bit) {
-                       test_msg("Testing bit pattern with offset failed\n");
-                       return -EINVAL;
-               }
+       ret = check_eb_bitmap(bitmap, eb, len);
+       if (ret) {
+               test_msg("Random bit pattern failed\n");
+               return ret;
        }
 
        return 0;
index 7508d3b427804c0cb634b565e0b625e99a2c0dfc..6e144048a72eedb206e902bf71fe19d6c6555c0e 100644 (file)
 #include "../transaction.h"
 
 struct free_space_extent {
-       u64 start, length;
+       u64 start;
+       u64 length;
 };
 
-/*
- * The test cases align their operations to this in order to hit some of the
- * edge cases in the bitmap code.
- */
-#define BITMAP_RANGE (BTRFS_FREE_SPACE_BITMAP_BITS * PAGE_SIZE)
-
 static int __check_free_space_extents(struct btrfs_trans_handle *trans,
                                      struct btrfs_fs_info *fs_info,
                                      struct btrfs_block_group_cache *cache,
                                      struct btrfs_path *path,
-                                     struct free_space_extent *extents,
+                                     const struct free_space_extent * const extents,
                                      unsigned int num_extents)
 {
        struct btrfs_free_space_info *info;
@@ -126,7 +121,7 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans,
                                    struct btrfs_fs_info *fs_info,
                                    struct btrfs_block_group_cache *cache,
                                    struct btrfs_path *path,
-                                   struct free_space_extent *extents,
+                                   const struct free_space_extent * const extents,
                                    unsigned int num_extents)
 {
        struct btrfs_free_space_info *info;
@@ -168,9 +163,10 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans,
 static int test_empty_block_group(struct btrfs_trans_handle *trans,
                                  struct btrfs_fs_info *fs_info,
                                  struct btrfs_block_group_cache *cache,
-                                 struct btrfs_path *path)
+                                 struct btrfs_path *path,
+                                 u32 alignment)
 {
-       struct free_space_extent extents[] = {
+       const struct free_space_extent extents[] = {
                {cache->key.objectid, cache->key.offset},
        };
 
@@ -181,9 +177,10 @@ static int test_empty_block_group(struct btrfs_trans_handle *trans,
 static int test_remove_all(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {};
+       const struct free_space_extent extents[] = {};
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
@@ -201,16 +198,17 @@ static int test_remove_all(struct btrfs_trans_handle *trans,
 static int test_remove_beginning(struct btrfs_trans_handle *trans,
                                 struct btrfs_fs_info *fs_info,
                                 struct btrfs_block_group_cache *cache,
-                                struct btrfs_path *path)
+                                struct btrfs_path *path,
+                                u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid + BITMAP_RANGE,
-                       cache->key.offset - BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid + alignment,
+                       cache->key.offset - alignment},
        };
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
-                                           cache->key.objectid, BITMAP_RANGE);
+                                           cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not remove free space\n");
                return ret;
@@ -224,17 +222,18 @@ static int test_remove_beginning(struct btrfs_trans_handle *trans,
 static int test_remove_end(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, cache->key.offset - BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, cache->key.offset - alignment},
        };
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
                                            cache->key.objectid +
-                                           cache->key.offset - BITMAP_RANGE,
-                                           BITMAP_RANGE);
+                                           cache->key.offset - alignment,
+                                           alignment);
        if (ret) {
                test_msg("Could not remove free space\n");
                return ret;
@@ -247,18 +246,19 @@ static int test_remove_end(struct btrfs_trans_handle *trans,
 static int test_remove_middle(struct btrfs_trans_handle *trans,
                              struct btrfs_fs_info *fs_info,
                              struct btrfs_block_group_cache *cache,
-                             struct btrfs_path *path)
+                             struct btrfs_path *path,
+                             u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, BITMAP_RANGE},
-               {cache->key.objectid + 2 * BITMAP_RANGE,
-                       cache->key.offset - 2 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, alignment},
+               {cache->key.objectid + 2 * alignment,
+                       cache->key.offset - 2 * alignment},
        };
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
-                                           cache->key.objectid + BITMAP_RANGE,
-                                           BITMAP_RANGE);
+                                           cache->key.objectid + alignment,
+                                           alignment);
        if (ret) {
                test_msg("Could not remove free space\n");
                return ret;
@@ -271,10 +271,11 @@ static int test_remove_middle(struct btrfs_trans_handle *trans,
 static int test_merge_left(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, 2 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, 2 * alignment},
        };
        int ret;
 
@@ -287,15 +288,15 @@ static int test_merge_left(struct btrfs_trans_handle *trans,
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid, BITMAP_RANGE);
+                                      cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -308,10 +309,11 @@ static int test_merge_left(struct btrfs_trans_handle *trans,
 static int test_merge_right(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid + BITMAP_RANGE, 2 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid + alignment, 2 * alignment},
        };
        int ret;
 
@@ -324,16 +326,16 @@ static int test_merge_right(struct btrfs_trans_handle *trans,
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 2 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 2 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -346,10 +348,11 @@ static int test_merge_right(struct btrfs_trans_handle *trans,
 static int test_merge_both(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, 3 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, 3 * alignment},
        };
        int ret;
 
@@ -362,23 +365,23 @@ static int test_merge_both(struct btrfs_trans_handle *trans,
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid, BITMAP_RANGE);
+                                      cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 2 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 2 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -391,12 +394,13 @@ static int test_merge_both(struct btrfs_trans_handle *trans,
 static int test_merge_none(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, BITMAP_RANGE},
-               {cache->key.objectid + 2 * BITMAP_RANGE, BITMAP_RANGE},
-               {cache->key.objectid + 4 * BITMAP_RANGE, BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, alignment},
+               {cache->key.objectid + 2 * alignment, alignment},
+               {cache->key.objectid + 4 * alignment, alignment},
        };
        int ret;
 
@@ -409,23 +413,23 @@ static int test_merge_none(struct btrfs_trans_handle *trans,
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid, BITMAP_RANGE);
+                                      cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 4 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 4 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 2 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 2 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -438,10 +442,11 @@ static int test_merge_none(struct btrfs_trans_handle *trans,
 typedef int (*test_func_t)(struct btrfs_trans_handle *,
                           struct btrfs_fs_info *,
                           struct btrfs_block_group_cache *,
-                          struct btrfs_path *);
+                          struct btrfs_path *,
+                          u32 alignment);
 
-static int run_test(test_func_t test_func, int bitmaps,
-               u32 sectorsize, u32 nodesize)
+static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize,
+                   u32 nodesize, u32 alignment)
 {
        struct btrfs_fs_info *fs_info;
        struct btrfs_root *root = NULL;
@@ -480,7 +485,7 @@ static int run_test(test_func_t test_func, int bitmaps,
        btrfs_set_header_nritems(root->node, 0);
        root->alloc_bytenr += 2 * nodesize;
 
-       cache = btrfs_alloc_dummy_block_group(8 * BITMAP_RANGE, sectorsize);
+       cache = btrfs_alloc_dummy_block_group(8 * alignment, sectorsize);
        if (!cache) {
                test_msg("Couldn't allocate dummy block group cache\n");
                ret = -ENOMEM;
@@ -514,7 +519,7 @@ static int run_test(test_func_t test_func, int bitmaps,
                }
        }
 
-       ret = test_func(&trans, root->fs_info, cache, path);
+       ret = test_func(&trans, root->fs_info, cache, path, alignment);
        if (ret)
                goto out;
 
@@ -539,15 +544,27 @@ out:
        return ret;
 }
 
-static int run_test_both_formats(test_func_t test_func,
-       u32 sectorsize, u32 nodesize)
+static int run_test_both_formats(test_func_t test_func, u32 sectorsize,
+                                u32 nodesize, u32 alignment)
 {
+       int test_ret = 0;
        int ret;
 
-       ret = run_test(test_func, 0, sectorsize, nodesize);
-       if (ret)
-               return ret;
-       return run_test(test_func, 1, sectorsize, nodesize);
+       ret = run_test(test_func, 0, sectorsize, nodesize, alignment);
+       if (ret) {
+               test_msg("%pf failed with extents, sectorsize=%u, nodesize=%u, alignment=%u\n",
+                        test_func, sectorsize, nodesize, alignment);
+               test_ret = ret;
+       }
+
+       ret = run_test(test_func, 1, sectorsize, nodesize, alignment);
+       if (ret) {
+               test_msg("%pf failed with bitmaps, sectorsize=%u, nodesize=%u, alignment=%u\n",
+                        test_func, sectorsize, nodesize, alignment);
+               test_ret = ret;
+       }
+
+       return test_ret;
 }
 
 int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize)
@@ -563,18 +580,30 @@ int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize)
                test_merge_both,
                test_merge_none,
        };
+       u32 bitmap_alignment;
+       int test_ret = 0;
        int i;
 
+       /*
+        * Align some operations to a page to flush out bugs in the extent
+        * buffer bitmap handling of highmem.
+        */
+       bitmap_alignment = BTRFS_FREE_SPACE_BITMAP_BITS * PAGE_SIZE;
+
        test_msg("Running free space tree tests\n");
        for (i = 0; i < ARRAY_SIZE(tests); i++) {
-               int ret = run_test_both_formats(tests[i], sectorsize,
-                       nodesize);
-               if (ret) {
-                       test_msg("%pf : sectorsize %u failed\n",
-                               tests[i], sectorsize);
-                       return ret;
-               }
+               int ret;
+
+               ret = run_test_both_formats(tests[i], sectorsize, nodesize,
+                                           sectorsize);
+               if (ret)
+                       test_ret = ret;
+
+               ret = run_test_both_formats(tests[i], sectorsize, nodesize,
+                                           bitmap_alignment);
+               if (ret)
+                       test_ret = ret;
        }
 
-       return 0;
+       return test_ret;
 }
index 6c58e13fed2f1d647e9b4cc58e1fc3660aff95d3..3d03e48a92139a4f67feeb072048ee5649006481 100644 (file)
@@ -152,6 +152,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
        list_for_each(tmp1, &cifs_tcp_ses_list) {
                server = list_entry(tmp1, struct TCP_Server_Info,
                                    tcp_ses_list);
+               seq_printf(m, "\nNumber of credits: %d", server->credits);
                i++;
                list_for_each(tmp2, &server->smb_ses_list) {
                        ses = list_entry(tmp2, struct cifs_ses,
index 1418daa03d959f7144f6ae0f0eabb0fdcb1f8d34..07ed81cf1552e6ae97be753de0720741e3f80068 100644 (file)
@@ -49,6 +49,7 @@
 #define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible
                                              * root mountable
                                              */
+#define CIFS_MOUNT_UID_FROM_ACL 0x2000000 /* try to get UID via special SID */
 
 struct cifs_sb_info {
        struct rb_root tlink_tree;
index 0065256881d8382ccc9974bff9f76b100e140643..57ff0756e30c690d139b97b31f546c3037f7ef7c 100644 (file)
@@ -36,7 +36,15 @@ struct smb_mnt_fs_info {
        __u64   cifs_posix_caps;
 } __packed;
 
+struct smb_snapshot_array {
+       __u32   number_of_snapshots;
+       __u32   number_of_snapshots_returned;
+       __u32   snapshot_array_size;
+       /*      snapshots[]; */
+} __packed;
+
 #define CIFS_IOCTL_MAGIC       0xCF
 #define CIFS_IOC_COPYCHUNK_FILE        _IOW(CIFS_IOCTL_MAGIC, 3, int)
 #define CIFS_IOC_SET_INTEGRITY  _IO(CIFS_IOCTL_MAGIC, 4)
 #define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info)
+#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
index 71e8a56e9479567e9f5ad0fb201a2ea3e91d6e99..15bac390dff945d7fa5d785f666b2b0291fb9f65 100644 (file)
@@ -42,6 +42,35 @@ static const struct cifs_sid sid_authusers = {
 /* group users */
 static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
 
+/* S-1-22-1 Unmapped Unix users */
+static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22},
+               {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/* S-1-22-2 Unmapped Unix groups */
+static const struct cifs_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22},
+               {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/*
+ * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
+ */
+
+/* S-1-5-88 MS NFS and Apple style UID/GID/mode */
+
+/* S-1-5-88-1 Unix uid */
+static const struct cifs_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5},
+       {cpu_to_le32(88),
+        cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/* S-1-5-88-2 Unix gid */
+static const struct cifs_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5},
+       {cpu_to_le32(88),
+        cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/* S-1-5-88-3 Unix mode */
+static const struct cifs_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5},
+       {cpu_to_le32(88),
+        cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
 static const struct cred *root_cred;
 
 static int
@@ -183,6 +212,62 @@ compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
        return 0; /* sids compare/match */
 }
 
+static bool
+is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group)
+{
+       int i;
+       int num_subauth;
+       const struct cifs_sid *pwell_known_sid;
+
+       if (!psid || (puid == NULL))
+               return false;
+
+       num_subauth = psid->num_subauth;
+
+       /* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */
+       if (num_subauth == 2) {
+               if (is_group)
+                       pwell_known_sid = &sid_unix_groups;
+               else
+                       pwell_known_sid = &sid_unix_users;
+       } else if (num_subauth == 3) {
+               if (is_group)
+                       pwell_known_sid = &sid_unix_NFS_groups;
+               else
+                       pwell_known_sid = &sid_unix_NFS_users;
+       } else
+               return false;
+
+       /* compare the revision */
+       if (psid->revision != pwell_known_sid->revision)
+               return false;
+
+       /* compare all of the six auth values */
+       for (i = 0; i < NUM_AUTHS; ++i) {
+               if (psid->authority[i] != pwell_known_sid->authority[i]) {
+                       cifs_dbg(FYI, "auth %d did not match\n", i);
+                       return false;
+               }
+       }
+
+       if (num_subauth == 2) {
+               if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0])
+                       return false;
+
+               *puid = le32_to_cpu(psid->sub_auth[1]);
+       } else /* 3 subauths, ie Windows/Mac style */ {
+               *puid = le32_to_cpu(psid->sub_auth[0]);
+               if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) ||
+                   (psid->sub_auth[1] != pwell_known_sid->sub_auth[1]))
+                       return false;
+
+               *puid = le32_to_cpu(psid->sub_auth[2]);
+       }
+
+       cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid);
+       return true; /* well known sid found, uid returned */
+}
+
 static void
 cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
 {
@@ -276,6 +361,43 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
                return -EIO;
        }
 
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) {
+               uint32_t unix_id;
+               bool is_group;
+
+               if (sidtype != SIDOWNER)
+                       is_group = true;
+               else
+                       is_group = false;
+
+               if (is_well_known_sid(psid, &unix_id, is_group) == false)
+                       goto try_upcall_to_get_id;
+
+               if (is_group) {
+                       kgid_t gid;
+                       gid_t id;
+
+                       id = (gid_t)unix_id;
+                       gid = make_kgid(&init_user_ns, id);
+                       if (gid_valid(gid)) {
+                               fgid = gid;
+                               goto got_valid_id;
+                       }
+               } else {
+                       kuid_t uid;
+                       uid_t id;
+
+                       id = (uid_t)unix_id;
+                       uid = make_kuid(&init_user_ns, id);
+                       if (uid_valid(uid)) {
+                               fuid = uid;
+                               goto got_valid_id;
+                       }
+               }
+               /* If unable to find uid/gid easily from SID try via upcall */
+       }
+
+try_upcall_to_get_id:
        sidstr = sid_to_key_str(psid, sidtype);
        if (!sidstr)
                return -ENOMEM;
@@ -329,6 +451,7 @@ out_revert_creds:
         * Note that we return 0 here unconditionally. If the mapping
         * fails then we just fall back to using the mnt_uid/mnt_gid.
         */
+got_valid_id:
        if (sidtype == SIDOWNER)
                fattr->cf_uid = fuid;
        else
index cca04e710421d83453dab921a01c881d1c35ebce..15261ba464c5023f82014751fee8efaf09abec5f 100644 (file)
@@ -64,15 +64,15 @@ unsigned int global_secflags = CIFSSEC_DEF;
 unsigned int sign_CIFS_PDUs = 1;
 static const struct super_operations cifs_super_ops;
 unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
-module_param(CIFSMaxBufSize, uint, 0);
+module_param(CIFSMaxBufSize, uint, 0444);
 MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header). "
                                 "Default: 16384 Range: 8192 to 130048");
 unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL;
-module_param(cifs_min_rcv, uint, 0);
+module_param(cifs_min_rcv, uint, 0444);
 MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: "
                                "1 to 64");
 unsigned int cifs_min_small = 30;
-module_param(cifs_min_small, uint, 0);
+module_param(cifs_min_small, uint, 0444);
 MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 "
                                 "Range: 2 to 256");
 unsigned int cifs_max_pending = CIFS_MAX_REQ;
@@ -271,7 +271,7 @@ cifs_alloc_inode(struct super_block *sb)
        cifs_inode->createtime = 0;
        cifs_inode->epoch = 0;
 #ifdef CONFIG_CIFS_SMB2
-       get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
+       generate_random_uuid(cifs_inode->lease_key);
 #endif
        /*
         * Can not set i_flags here - they get immediately overwritten to zero
@@ -469,6 +469,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
                seq_puts(s, ",posixpaths");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)
                seq_puts(s, ",setuids");
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL)
+               seq_puts(s, ",idsfromsid");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
                seq_puts(s, ",serverino");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
@@ -1262,7 +1264,6 @@ init_cifs(void)
        GlobalTotalActiveXid = 0;
        GlobalMaxActiveXid = 0;
        spin_lock_init(&cifs_tcp_ses_lock);
-       spin_lock_init(&cifs_file_list_lock);
        spin_lock_init(&GlobalMid_Lock);
 
        get_random_bytes(&cifs_lock_secret, sizeof(cifs_lock_secret));
index 8f1d8c1e72bece412a926d0e20b9bdc95a7fa9ee..1f17f6bd7a601c56e40a6393c25eaee892e40a0f 100644 (file)
 #define SMB_ECHO_INTERVAL_MAX 600
 #define SMB_ECHO_INTERVAL_DEFAULT 60
 
+/*
+ * Default number of credits to keep available for SMB3.
+ * This value is chosen somewhat arbitrarily. The Windows client
+ * defaults to 128 credits, the Windows server allows clients up to
+ * 512 credits (or 8K for later versions), and the NetApp server
+ * does not limit clients at all.  Choose a high enough default value
+ * such that the client shouldn't limit performance, but allow mount
+ * to override (until you approach 64K, where we limit credits to 65000
+ * to reduce possibility of seeing more server credit overflow bugs.
+ */
+#define SMB2_MAX_CREDITS_AVAILABLE 32000
+
 #include "cifspdu.h"
 
 #ifndef XATTR_DOS_ATTRIB
@@ -376,6 +388,8 @@ struct smb_version_operations {
        int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *);
        int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
                             struct cifsFileInfo *src_file);
+       int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
+                            struct cifsFileInfo *src_file, void __user *);
        int (*query_mf_symlink)(unsigned int, struct cifs_tcon *,
                                struct cifs_sb_info *, const unsigned char *,
                                char *, unsigned int *);
@@ -464,6 +478,7 @@ struct smb_vol {
        bool retry:1;
        bool intr:1;
        bool setuids:1;
+       bool setuidfromacl:1;
        bool override_uid:1;
        bool override_gid:1;
        bool dynperm:1;
@@ -510,6 +525,7 @@ struct smb_vol {
        struct sockaddr_storage srcaddr; /* allow binding to a local IP */
        struct nls_table *local_nls;
        unsigned int echo_interval; /* echo interval in secs */
+       unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
 };
 
 #define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \
@@ -567,7 +583,8 @@ struct TCP_Server_Info {
        bool noblocksnd;                /* use blocking sendmsg */
        bool noautotune;                /* do not autotune send buf sizes */
        bool tcp_nodelay;
-       int credits;  /* send no more requests at once */
+       unsigned int credits;  /* send no more requests at once */
+       unsigned int max_credits; /* can override large 32000 default at mnt */
        unsigned int in_flight;  /* number of requests on the wire to server */
        spinlock_t req_lock;  /* protect the two values above */
        struct mutex srv_mutex;
@@ -833,6 +850,7 @@ struct cifs_tcon {
        struct list_head tcon_list;
        int tc_count;
        struct list_head openFileList;
+       spinlock_t open_file_lock; /* protects list above */
        struct cifs_ses *ses;   /* pointer to session associated with */
        char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
        char *nativeFileSystem;
@@ -889,7 +907,7 @@ struct cifs_tcon {
 #endif /* CONFIG_CIFS_STATS2 */
        __u64    bytes_read;
        __u64    bytes_written;
-       spinlock_t stat_lock;
+       spinlock_t stat_lock;  /* protects the two fields above */
 #endif /* CONFIG_CIFS_STATS */
        FILE_SYSTEM_DEVICE_INFO fsDevInfo;
        FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
@@ -1040,20 +1058,24 @@ struct cifs_fid_locks {
 };
 
 struct cifsFileInfo {
+       /* following two lists are protected by tcon->open_file_lock */
        struct list_head tlist; /* pointer to next fid owned by tcon */
        struct list_head flist; /* next fid (file instance) for this inode */
+       /* lock list below protected by cifsi->lock_sem */
        struct cifs_fid_locks *llist;   /* brlocks held by this fid */
        kuid_t uid;             /* allows finding which FileInfo structure */
        __u32 pid;              /* process id who opened file */
        struct cifs_fid fid;    /* file id from remote */
+       struct list_head rlist; /* reconnect list */
        /* BB add lock scope info here if needed */ ;
        /* lock scope id (0 if none) */
        struct dentry *dentry;
-       unsigned int f_flags;
        struct tcon_link *tlink;
+       unsigned int f_flags;
        bool invalidHandle:1;   /* file closed via session abend */
        bool oplock_break_cancelled:1;
-       int count;              /* refcount protected by cifs_file_list_lock */
+       int count;
+       spinlock_t file_info_lock; /* protects four flag/count fields above */
        struct mutex fh_mutex; /* prevents reopen race after dead ses*/
        struct cifs_search_info srch_inf;
        struct work_struct oplock_break; /* work for oplock breaks */
@@ -1120,7 +1142,7 @@ struct cifs_writedata {
 
 /*
  * Take a reference on the file private data. Must be called with
- * cifs_file_list_lock held.
+ * cfile->file_info_lock held.
  */
 static inline void
 cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
@@ -1514,8 +1536,10 @@ require use of the stronger protocol */
  *  GlobalMid_Lock protects:
  *     list operations on pending_mid_q and oplockQ
  *      updates to XID counters, multiplex id  and SMB sequence numbers
- *  cifs_file_list_lock protects:
- *     list operations on tcp and SMB session lists and tCon lists
+ *  tcp_ses_lock protects:
+ *     list operations on tcp and SMB session lists
+ *  tcon->open_file_lock protects the list of open files hanging off the tcon
+ *  cfile->file_info_lock protects counters and fields in cifs file struct
  *  f_owner.lock protects certain per file struct operations
  *  mapping->page_lock protects certain per page operations
  *
@@ -1547,18 +1571,12 @@ GLOBAL_EXTERN struct list_head          cifs_tcp_ses_list;
  * tcp session, and the list of tcon's per smb session. It also protects
  * the reference counters for the server, smb session, and tcon. Finally,
  * changes to the tcon->tidStatus should be done while holding this lock.
+ * generally the locks should be taken in order tcp_ses_lock before
+ * tcon->open_file_lock and that before file->file_info_lock since the
+ * structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file
  */
 GLOBAL_EXTERN spinlock_t               cifs_tcp_ses_lock;
 
-/*
- * This lock protects the cifs_file->llist and cifs_file->flist
- * list operations, and updates to some flags (cifs_file->invalidHandle)
- * It will be moved to either use the tcon->stat_lock or equivalent later.
- * If cifs_tcp_ses_lock and the lock below are both needed to be held, then
- * the cifs_tcp_ses_lock must be grabbed first and released last.
- */
-GLOBAL_EXTERN spinlock_t       cifs_file_list_lock;
-
 #ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
 /* Outstanding dir notify requests */
 GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
index 4ead72a001f974d4e3b18dedf7a2466f4426c0e9..ced0e42ce460963104d89bb8b941b899ceb33dea 100644 (file)
@@ -193,6 +193,8 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data,
 extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
 extern void cifs_umount(struct cifs_sb_info *);
 extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
+extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon);
+
 extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
                                    __u64 length, __u8 type,
                                    struct cifsLockInfo **conf_lock,
index f82d2823622f334cf83e677c042ead92a0379b0a..3f3185febc585f93fb9f88a0b0e96ec49b636f4a 100644 (file)
@@ -98,13 +98,13 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
        struct list_head *tmp1;
 
        /* list all files open on tree connection and mark them invalid */
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
        list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
                open_file = list_entry(tmp, struct cifsFileInfo, tlist);
                open_file->invalidHandle = true;
                open_file->oplock_break_cancelled = true;
        }
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tcon->open_file_lock);
        /*
         * BB Add call to invalidate_inodes(sb) for all superblocks mounted
         * to this tcon.
index 2e4f4bad8b1e9bf75cc76074914b8c2c8f63a3ff..aab5227979e2ee27c1858b47f9bf33c275f27633 100644 (file)
@@ -63,7 +63,6 @@ extern mempool_t *cifs_req_poolp;
 #define TLINK_IDLE_EXPIRE      (600 * HZ)
 
 enum {
-
        /* Mount options that take no arguments */
        Opt_user_xattr, Opt_nouser_xattr,
        Opt_forceuid, Opt_noforceuid,
@@ -76,7 +75,7 @@ enum {
        Opt_noposixpaths, Opt_nounix,
        Opt_nocase,
        Opt_brl, Opt_nobrl,
-       Opt_forcemandatorylock, Opt_setuids,
+       Opt_forcemandatorylock, Opt_setuidfromacl, Opt_setuids,
        Opt_nosetuids, Opt_dynperm, Opt_nodynperm,
        Opt_nohard, Opt_nosoft,
        Opt_nointr, Opt_intr,
@@ -95,7 +94,7 @@ enum {
        Opt_cruid, Opt_gid, Opt_file_mode,
        Opt_dirmode, Opt_port,
        Opt_rsize, Opt_wsize, Opt_actimeo,
-       Opt_echo_interval,
+       Opt_echo_interval, Opt_max_credits,
 
        /* Mount options which take string value */
        Opt_user, Opt_pass, Opt_ip,
@@ -148,6 +147,7 @@ static const match_table_t cifs_mount_option_tokens = {
        { Opt_forcemandatorylock, "forcemand" },
        { Opt_setuids, "setuids" },
        { Opt_nosetuids, "nosetuids" },
+       { Opt_setuidfromacl, "idsfromsid" },
        { Opt_dynperm, "dynperm" },
        { Opt_nodynperm, "nodynperm" },
        { Opt_nohard, "nohard" },
@@ -190,6 +190,7 @@ static const match_table_t cifs_mount_option_tokens = {
        { Opt_wsize, "wsize=%s" },
        { Opt_actimeo, "actimeo=%s" },
        { Opt_echo_interval, "echo_interval=%s" },
+       { Opt_max_credits, "max_credits=%s" },
 
        { Opt_blank_user, "user=" },
        { Opt_blank_user, "username=" },
@@ -1376,6 +1377,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                case Opt_nosetuids:
                        vol->setuids = 0;
                        break;
+               case Opt_setuidfromacl:
+                       vol->setuidfromacl = 1;
+                       break;
                case Opt_dynperm:
                        vol->dynperm = true;
                        break;
@@ -1586,6 +1590,15 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        }
                        vol->echo_interval = option;
                        break;
+               case Opt_max_credits:
+                       if (get_option_ul(args, &option) || (option < 20) ||
+                           (option > 60000)) {
+                               cifs_dbg(VFS, "%s: Invalid max_credits value\n",
+                                        __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->max_credits = option;
+                       break;
 
                /* String Arguments */
 
@@ -2163,7 +2176,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
        memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
                sizeof(tcp_ses->dstaddr));
 #ifdef CONFIG_CIFS_SMB2
-       get_random_bytes(tcp_ses->client_guid, SMB2_CLIENT_GUID_SIZE);
+       generate_random_uuid(tcp_ses->client_guid);
 #endif
        /*
         * at this point we are the only ones with the pointer
@@ -3270,6 +3283,8 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
        if (pvolume_info->setuids)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
+       if (pvolume_info->setuidfromacl)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL;
        if (pvolume_info->server_ino)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
        if (pvolume_info->remap)
@@ -3598,7 +3613,11 @@ try_mount_again:
                bdi_destroy(&cifs_sb->bdi);
                goto out;
        }
-
+       if ((volume_info->max_credits < 20) ||
+            (volume_info->max_credits > 60000))
+               server->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
+       else
+               server->max_credits = volume_info->max_credits;
        /* get a reference to a SMB session */
        ses = cifs_get_smb_ses(server, volume_info);
        if (IS_ERR(ses)) {
@@ -3688,14 +3707,16 @@ remote_path_check:
                        goto mount_fail_check;
                }
 
-               rc = cifs_are_all_path_components_accessible(server,
+               if (rc != -EREMOTE) {
+                       rc = cifs_are_all_path_components_accessible(server,
                                                             xid, tcon, cifs_sb,
                                                             full_path);
-               if (rc != 0) {
-                       cifs_dbg(VFS, "cannot query dirs between root and final path, "
-                                "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
-                       rc = 0;
+                       if (rc != 0) {
+                               cifs_dbg(VFS, "cannot query dirs between root and final path, "
+                                        "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
+                               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+                               rc = 0;
+                       }
                }
                kfree(full_path);
        }
index a95fe8b1afe93de6b091ce2015cd6f9dbb2f3f23..7f5f6176c6f15caff307e078320122141c119ab9 100644 (file)
@@ -305,6 +305,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
        cfile->tlink = cifs_get_tlink(tlink);
        INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
        mutex_init(&cfile->fh_mutex);
+       spin_lock_init(&cfile->file_info_lock);
 
        cifs_sb_active(inode->i_sb);
 
@@ -317,7 +318,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
                oplock = 0;
        }
 
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
        if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)
                oplock = fid->pending_open->oplock;
        list_del(&fid->pending_open->olist);
@@ -326,12 +327,13 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
        server->ops->set_fid(cfile, fid, oplock);
 
        list_add(&cfile->tlist, &tcon->openFileList);
+
        /* if readable file instance put first in list*/
        if (file->f_mode & FMODE_READ)
                list_add(&cfile->flist, &cinode->openFileList);
        else
                list_add_tail(&cfile->flist, &cinode->openFileList);
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tcon->open_file_lock);
 
        if (fid->purge_cache)
                cifs_zap_mapping(inode);
@@ -343,16 +345,16 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
 struct cifsFileInfo *
 cifsFileInfo_get(struct cifsFileInfo *cifs_file)
 {
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&cifs_file->file_info_lock);
        cifsFileInfo_get_locked(cifs_file);
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&cifs_file->file_info_lock);
        return cifs_file;
 }
 
 /*
  * Release a reference on the file private data. This may involve closing
  * the filehandle out on the server. Must be called without holding
- * cifs_file_list_lock.
+ * tcon->open_file_lock and cifs_file->file_info_lock.
  */
 void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
 {
@@ -367,11 +369,15 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
        struct cifs_pending_open open;
        bool oplock_break_cancelled;
 
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
+
+       spin_lock(&cifs_file->file_info_lock);
        if (--cifs_file->count > 0) {
-               spin_unlock(&cifs_file_list_lock);
+               spin_unlock(&cifs_file->file_info_lock);
+               spin_unlock(&tcon->open_file_lock);
                return;
        }
+       spin_unlock(&cifs_file->file_info_lock);
 
        if (server->ops->get_lease_key)
                server->ops->get_lease_key(inode, &fid);
@@ -395,7 +401,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
                        set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags);
                cifs_set_oplock_level(cifsi, 0);
        }
-       spin_unlock(&cifs_file_list_lock);
+
+       spin_unlock(&tcon->open_file_lock);
 
        oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break);
 
@@ -732,6 +739,15 @@ reopen_success:
         * to the server to get the new inode info.
         */
 
+       /*
+        * If the server returned a read oplock and we have mandatory brlocks,
+        * set oplock level to None.
+        */
+       if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) {
+               cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n");
+               oplock = 0;
+       }
+
        server->ops->set_fid(cfile, &cfile->fid, oplock);
        if (oparms.reconnect)
                cifs_relock_file(cfile);
@@ -753,6 +769,36 @@ int cifs_close(struct inode *inode, struct file *file)
        return 0;
 }
 
+void
+cifs_reopen_persistent_handles(struct cifs_tcon *tcon)
+{
+       struct cifsFileInfo *open_file;
+       struct list_head *tmp;
+       struct list_head *tmp1;
+       struct list_head tmp_list;
+
+       cifs_dbg(FYI, "Reopen persistent handles");
+       INIT_LIST_HEAD(&tmp_list);
+
+       /* list all files open on tree connection, reopen resilient handles  */
+       spin_lock(&tcon->open_file_lock);
+       list_for_each(tmp, &tcon->openFileList) {
+               open_file = list_entry(tmp, struct cifsFileInfo, tlist);
+               if (!open_file->invalidHandle)
+                       continue;
+               cifsFileInfo_get(open_file);
+               list_add_tail(&open_file->rlist, &tmp_list);
+       }
+       spin_unlock(&tcon->open_file_lock);
+
+       list_for_each_safe(tmp, tmp1, &tmp_list) {
+               open_file = list_entry(tmp, struct cifsFileInfo, rlist);
+               cifs_reopen_file(open_file, false /* do not flush */);
+               list_del_init(&open_file->rlist);
+               cifsFileInfo_put(open_file);
+       }
+}
+
 int cifs_closedir(struct inode *inode, struct file *file)
 {
        int rc = 0;
@@ -772,10 +818,10 @@ int cifs_closedir(struct inode *inode, struct file *file)
        server = tcon->ses->server;
 
        cifs_dbg(FYI, "Freeing private data in close dir\n");
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&cfile->file_info_lock);
        if (server->ops->dir_needs_close(cfile)) {
                cfile->invalidHandle = true;
-               spin_unlock(&cifs_file_list_lock);
+               spin_unlock(&cfile->file_info_lock);
                if (server->ops->close_dir)
                        rc = server->ops->close_dir(xid, tcon, &cfile->fid);
                else
@@ -784,7 +830,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
                /* not much we can do if it fails anyway, ignore rc */
                rc = 0;
        } else
-               spin_unlock(&cifs_file_list_lock);
+               spin_unlock(&cfile->file_info_lock);
 
        buf = cfile->srch_inf.ntwrk_buf_start;
        if (buf) {
@@ -1728,12 +1774,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
 {
        struct cifsFileInfo *open_file = NULL;
        struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
+       struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
 
        /* only filter by fsuid on multiuser mounts */
        if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
                fsuid_only = false;
 
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
        /* we could simply get the first_list_entry since write-only entries
           are always at the end of the list but since the first entry might
           have a close pending, we go through the whole list */
@@ -1744,8 +1791,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
                        if (!open_file->invalidHandle) {
                                /* found a good file */
                                /* lock it so it will not be closed on us */
-                               cifsFileInfo_get_locked(open_file);
-                               spin_unlock(&cifs_file_list_lock);
+                               cifsFileInfo_get(open_file);
+                               spin_unlock(&tcon->open_file_lock);
                                return open_file;
                        } /* else might as well continue, and look for
                             another, or simply have the caller reopen it
@@ -1753,7 +1800,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
                } else /* write only file */
                        break; /* write only files are last so must be done */
        }
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tcon->open_file_lock);
        return NULL;
 }
 
@@ -1762,6 +1809,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
 {
        struct cifsFileInfo *open_file, *inv_file = NULL;
        struct cifs_sb_info *cifs_sb;
+       struct cifs_tcon *tcon;
        bool any_available = false;
        int rc;
        unsigned int refind = 0;
@@ -1777,15 +1825,16 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
        }
 
        cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
+       tcon = cifs_sb_master_tcon(cifs_sb);
 
        /* only filter by fsuid on multiuser mounts */
        if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
                fsuid_only = false;
 
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
 refind_writable:
        if (refind > MAX_REOPEN_ATT) {
-               spin_unlock(&cifs_file_list_lock);
+               spin_unlock(&tcon->open_file_lock);
                return NULL;
        }
        list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
@@ -1796,8 +1845,8 @@ refind_writable:
                if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
                        if (!open_file->invalidHandle) {
                                /* found a good writable file */
-                               cifsFileInfo_get_locked(open_file);
-                               spin_unlock(&cifs_file_list_lock);
+                               cifsFileInfo_get(open_file);
+                               spin_unlock(&tcon->open_file_lock);
                                return open_file;
                        } else {
                                if (!inv_file)
@@ -1813,24 +1862,24 @@ refind_writable:
 
        if (inv_file) {
                any_available = false;
-               cifsFileInfo_get_locked(inv_file);
+               cifsFileInfo_get(inv_file);
        }
 
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tcon->open_file_lock);
 
        if (inv_file) {
                rc = cifs_reopen_file(inv_file, false);
                if (!rc)
                        return inv_file;
                else {
-                       spin_lock(&cifs_file_list_lock);
+                       spin_lock(&tcon->open_file_lock);
                        list_move_tail(&inv_file->flist,
                                        &cifs_inode->openFileList);
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&tcon->open_file_lock);
                        cifsFileInfo_put(inv_file);
-                       spin_lock(&cifs_file_list_lock);
                        ++refind;
                        inv_file = NULL;
+                       spin_lock(&tcon->open_file_lock);
                        goto refind_writable;
                }
        }
@@ -3612,15 +3661,17 @@ static int cifs_readpage(struct file *file, struct page *page)
 static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
 {
        struct cifsFileInfo *open_file;
+       struct cifs_tcon *tcon =
+               cifs_sb_master_tcon(CIFS_SB(cifs_inode->vfs_inode.i_sb));
 
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
        list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
                if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&tcon->open_file_lock);
                        return 1;
                }
        }
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tcon->open_file_lock);
        return 0;
 }
 
index 7a3b84e300f8978b80baf43d834feb7fab5d0cfa..9f51b81119f2b204361f25d86dcfd350a8917700 100644 (file)
@@ -189,7 +189,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
        xid = get_xid();
 
        cifs_sb = CIFS_SB(inode->i_sb);
-
+       cifs_dbg(VFS, "cifs ioctl 0x%x\n", command);
        switch (command) {
                case FS_IOC_GETFLAGS:
                        if (pSMBFile == NULL)
@@ -267,11 +267,23 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
                        tcon = tlink_tcon(pSMBFile->tlink);
                        rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
                        break;
+               case CIFS_ENUMERATE_SNAPSHOTS:
+                       if (arg == 0) {
+                               rc = -EINVAL;
+                               goto cifs_ioc_exit;
+                       }
+                       tcon = tlink_tcon(pSMBFile->tlink);
+                       if (tcon->ses->server->ops->enum_snapshots)
+                               rc = tcon->ses->server->ops->enum_snapshots(xid, tcon,
+                                               pSMBFile, (void __user *)arg);
+                       else
+                               rc = -EOPNOTSUPP;
+                       break;
                default:
                        cifs_dbg(FYI, "unsupported ioctl\n");
                        break;
        }
-
+cifs_ioc_exit:
        free_xid(xid);
        return rc;
 }
index 813fe13c2ae175869cdc6db06a19392d09d7f05f..c6729156f9a00cf69938f8ec66c92fde561849ab 100644 (file)
@@ -120,6 +120,7 @@ tconInfoAlloc(void)
                ++ret_buf->tc_count;
                INIT_LIST_HEAD(&ret_buf->openFileList);
                INIT_LIST_HEAD(&ret_buf->tcon_list);
+               spin_lock_init(&ret_buf->open_file_lock);
 #ifdef CONFIG_CIFS_STATS
                spin_lock_init(&ret_buf->stat_lock);
 #endif
@@ -465,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
                                continue;
 
                        cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
-                       spin_lock(&cifs_file_list_lock);
+                       spin_lock(&tcon->open_file_lock);
                        list_for_each(tmp2, &tcon->openFileList) {
                                netfile = list_entry(tmp2, struct cifsFileInfo,
                                                     tlist);
@@ -495,11 +496,11 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
                                           &netfile->oplock_break);
                                netfile->oplock_break_cancelled = false;
 
-                               spin_unlock(&cifs_file_list_lock);
+                               spin_unlock(&tcon->open_file_lock);
                                spin_unlock(&cifs_tcp_ses_lock);
                                return true;
                        }
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&tcon->open_file_lock);
                        spin_unlock(&cifs_tcp_ses_lock);
                        cifs_dbg(FYI, "No matching file for oplock break\n");
                        return true;
@@ -613,9 +614,9 @@ backup_cred(struct cifs_sb_info *cifs_sb)
 void
 cifs_del_pending_open(struct cifs_pending_open *open)
 {
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tlink_tcon(open->tlink)->open_file_lock);
        list_del(&open->olist);
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
 }
 
 void
@@ -635,7 +636,7 @@ void
 cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
                      struct cifs_pending_open *open)
 {
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tlink_tcon(tlink)->open_file_lock);
        cifs_add_pending_open_locked(fid, tlink, open);
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
 }
index 65cf85dcda09bd773886c8aa6a86d82af1a033b5..8f6a2a5863b9d9275bfb6afb00fc16b867101275 100644 (file)
@@ -597,14 +597,14 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
             is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
                /* close and restart search */
                cifs_dbg(FYI, "search backing up - close and restart search\n");
-               spin_lock(&cifs_file_list_lock);
+               spin_lock(&cfile->file_info_lock);
                if (server->ops->dir_needs_close(cfile)) {
                        cfile->invalidHandle = true;
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&cfile->file_info_lock);
                        if (server->ops->close_dir)
                                server->ops->close_dir(xid, tcon, &cfile->fid);
                } else
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&cfile->file_info_lock);
                if (cfile->srch_inf.ntwrk_buf_start) {
                        cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n");
                        if (cfile->srch_inf.smallBuf)
index 4f0231e685a922efcb97a6c5905bfedc7ca7ece6..1238cd3552f9cc8e8f8fc560117af37cd224aa13 100644 (file)
@@ -266,9 +266,15 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
        struct tcon_link *tlink;
        int rc;
 
+       if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
+           (buf->LastWriteTime == 0) && (buf->ChangeTime) &&
+           (buf->Attributes == 0))
+               return 0; /* would be a no op, no sense sending this */
+
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink))
                return PTR_ERR(tlink);
+
        rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path,
                                FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf,
                                SMB2_OP_SET_INFO);
index 389fb9f8c84e22308ac5fcb44c27f005bee6e27c..3d383489b9cf313395e94a0bff4a719501b4b309 100644 (file)
@@ -549,19 +549,19 @@ smb2_is_valid_lease_break(char *buffer)
                list_for_each(tmp1, &server->smb_ses_list) {
                        ses = list_entry(tmp1, struct cifs_ses, smb_ses_list);
 
-                       spin_lock(&cifs_file_list_lock);
                        list_for_each(tmp2, &ses->tcon_list) {
                                tcon = list_entry(tmp2, struct cifs_tcon,
                                                  tcon_list);
+                               spin_lock(&tcon->open_file_lock);
                                cifs_stats_inc(
                                    &tcon->stats.cifs_stats.num_oplock_brks);
                                if (smb2_tcon_has_lease(tcon, rsp, lw)) {
-                                       spin_unlock(&cifs_file_list_lock);
+                                       spin_unlock(&tcon->open_file_lock);
                                        spin_unlock(&cifs_tcp_ses_lock);
                                        return true;
                                }
+                               spin_unlock(&tcon->open_file_lock);
                        }
-                       spin_unlock(&cifs_file_list_lock);
                }
        }
        spin_unlock(&cifs_tcp_ses_lock);
@@ -603,7 +603,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
                        tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
 
                        cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
-                       spin_lock(&cifs_file_list_lock);
+                       spin_lock(&tcon->open_file_lock);
                        list_for_each(tmp2, &tcon->openFileList) {
                                cfile = list_entry(tmp2, struct cifsFileInfo,
                                                     tlist);
@@ -615,7 +615,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
 
                                cifs_dbg(FYI, "file id match, oplock break\n");
                                cinode = CIFS_I(d_inode(cfile->dentry));
-
+                               spin_lock(&cfile->file_info_lock);
                                if (!CIFS_CACHE_WRITE(cinode) &&
                                    rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE)
                                        cfile->oplock_break_cancelled = true;
@@ -637,14 +637,14 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
                                        clear_bit(
                                           CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
                                           &cinode->flags);
-
+                               spin_unlock(&cfile->file_info_lock);
                                queue_work(cifsiod_wq, &cfile->oplock_break);
 
-                               spin_unlock(&cifs_file_list_lock);
+                               spin_unlock(&tcon->open_file_lock);
                                spin_unlock(&cifs_tcp_ses_lock);
                                return true;
                        }
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&tcon->open_file_lock);
                        spin_unlock(&cifs_tcp_ses_lock);
                        cifs_dbg(FYI, "No matching file for oplock break\n");
                        return true;
index d203c0329626cd41ee05fd75da9ee4439304bdc1..5d456ebb381386e5299cf61123400f4985480e9a 100644 (file)
@@ -28,6 +28,7 @@
 #include "cifs_unicode.h"
 #include "smb2status.h"
 #include "smb2glob.h"
+#include "cifs_ioctl.h"
 
 static int
 change_conf(struct TCP_Server_Info *server)
@@ -70,6 +71,10 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
        spin_lock(&server->req_lock);
        val = server->ops->get_credits_field(server, optype);
        *val += add;
+       if (*val > 65000) {
+               *val = 65000; /* Don't get near 64K credits, avoid srv bugs */
+               printk_once(KERN_WARNING "server overflowed SMB3 credits\n");
+       }
        server->in_flight--;
        if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
                rc = change_conf(server);
@@ -287,7 +292,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
                cifs_dbg(FYI, "Link Speed %lld\n",
                        le64_to_cpu(out_buf->LinkSpeed));
        }
-
+       kfree(out_buf);
        return rc;
 }
 #endif /* STATS2 */
@@ -541,6 +546,7 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
        server->ops->set_oplock_level(cinode, oplock, fid->epoch,
                                      &fid->purge_cache);
        cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
+       memcpy(cfile->fid.create_guid, fid->create_guid, 16);
 }
 
 static void
@@ -699,6 +705,7 @@ smb2_clone_range(const unsigned int xid,
 
 cchunk_out:
        kfree(pcchunk);
+       kfree(retbuf);
        return rc;
 }
 
@@ -823,7 +830,6 @@ smb2_duplicate_extents(const unsigned int xid,
 {
        int rc;
        unsigned int ret_data_len;
-       char *retbuf = NULL;
        struct duplicate_extents_to_file dup_ext_buf;
        struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
 
@@ -849,7 +855,7 @@ smb2_duplicate_extents(const unsigned int xid,
                        FSCTL_DUPLICATE_EXTENTS_TO_FILE,
                        true /* is_fsctl */, (char *)&dup_ext_buf,
                        sizeof(struct duplicate_extents_to_file),
-                       (char **)&retbuf,
+                       NULL,
                        &ret_data_len);
 
        if (ret_data_len > 0)
@@ -872,7 +878,6 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
                   struct cifsFileInfo *cfile)
 {
        struct fsctl_set_integrity_information_req integr_info;
-       char *retbuf = NULL;
        unsigned int ret_data_len;
 
        integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED);
@@ -884,9 +889,53 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
                        FSCTL_SET_INTEGRITY_INFORMATION,
                        true /* is_fsctl */, (char *)&integr_info,
                        sizeof(struct fsctl_set_integrity_information_req),
+                       NULL,
+                       &ret_data_len);
+
+}
+
+static int
+smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
+                  struct cifsFileInfo *cfile, void __user *ioc_buf)
+{
+       char *retbuf = NULL;
+       unsigned int ret_data_len = 0;
+       int rc;
+       struct smb_snapshot_array snapshot_in;
+
+       rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+                       cfile->fid.volatile_fid,
+                       FSCTL_SRV_ENUMERATE_SNAPSHOTS,
+                       true /* is_fsctl */, NULL, 0 /* no input data */,
                        (char **)&retbuf,
                        &ret_data_len);
+       cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n",
+                       rc, ret_data_len);
+       if (rc)
+               return rc;
 
+       if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) {
+               /* Fixup buffer */
+               if (copy_from_user(&snapshot_in, ioc_buf,
+                   sizeof(struct smb_snapshot_array))) {
+                       rc = -EFAULT;
+                       kfree(retbuf);
+                       return rc;
+               }
+               if (snapshot_in.snapshot_array_size < sizeof(struct smb_snapshot_array)) {
+                       rc = -ERANGE;
+                       return rc;
+               }
+
+               if (ret_data_len > snapshot_in.snapshot_array_size)
+                       ret_data_len = snapshot_in.snapshot_array_size;
+
+               if (copy_to_user(ioc_buf, retbuf, ret_data_len))
+                       rc = -EFAULT;
+       }
+
+       kfree(retbuf);
+       return rc;
 }
 
 static int
@@ -1041,7 +1090,7 @@ smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid)
 static void
 smb2_new_lease_key(struct cifs_fid *fid)
 {
-       get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
+       generate_random_uuid(fid->lease_key);
 }
 
 #define SMB2_SYMLINK_STRUCT_SIZE \
@@ -1654,6 +1703,7 @@ struct smb_version_operations smb21_operations = {
        .clone_range = smb2_clone_range,
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
+       .enum_snapshots = smb3_enum_snapshots,
 };
 
 struct smb_version_operations smb30_operations = {
@@ -1740,6 +1790,7 @@ struct smb_version_operations smb30_operations = {
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
        .fallocate = smb3_fallocate,
+       .enum_snapshots = smb3_enum_snapshots,
 };
 
 #ifdef CONFIG_CIFS_SMB311
@@ -1827,6 +1878,7 @@ struct smb_version_operations smb311_operations = {
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
        .fallocate = smb3_fallocate,
+       .enum_snapshots = smb3_enum_snapshots,
 };
 #endif /* CIFS_SMB311 */
 
index 29e06db5f187bea7d4f5ce29d2cf5c0faad6ae09..5ca5ea4668a1482ef9643cd525ded6ebbaa7e326 100644 (file)
@@ -100,7 +100,21 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
        hdr->ProtocolId = SMB2_PROTO_NUMBER;
        hdr->StructureSize = cpu_to_le16(64);
        hdr->Command = smb2_cmd;
-       hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */
+       if (tcon && tcon->ses && tcon->ses->server) {
+               struct TCP_Server_Info *server = tcon->ses->server;
+
+               spin_lock(&server->req_lock);
+               /* Request up to 2 credits but don't go over the limit. */
+               if (server->credits >= server->max_credits)
+                       hdr->CreditRequest = cpu_to_le16(0);
+               else
+                       hdr->CreditRequest = cpu_to_le16(
+                               min_t(int, server->max_credits -
+                                               server->credits, 2));
+               spin_unlock(&server->req_lock);
+       } else {
+               hdr->CreditRequest = cpu_to_le16(2);
+       }
        hdr->ProcessId = cpu_to_le32((__u16)current->tgid);
 
        if (!tcon)
@@ -236,8 +250,13 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
        }
 
        cifs_mark_open_files_invalid(tcon);
+
        rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage);
        mutex_unlock(&tcon->ses->session_mutex);
+
+       if (tcon->use_persistent)
+               cifs_reopen_persistent_handles(tcon);
+
        cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
        if (rc)
                goto out;
@@ -574,59 +593,42 @@ vneg_out:
        return -EIO;
 }
 
-int
-SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
-               const struct nls_table *nls_cp)
+struct SMB2_sess_data {
+       unsigned int xid;
+       struct cifs_ses *ses;
+       struct nls_table *nls_cp;
+       void (*func)(struct SMB2_sess_data *);
+       int result;
+       u64 previous_session;
+
+       /* we will send the SMB in three pieces:
+        * a fixed length beginning part, an optional
+        * SPNEGO blob (which can be zero length), and a
+        * last part which will include the strings
+        * and rest of bcc area. This allows us to avoid
+        * a large buffer 17K allocation
+        */
+       int buf0_type;
+       struct kvec iov[2];
+};
+
+static int
+SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
 {
+       int rc;
+       struct cifs_ses *ses = sess_data->ses;
        struct smb2_sess_setup_req *req;
-       struct smb2_sess_setup_rsp *rsp = NULL;
-       struct kvec iov[2];
-       int rc = 0;
-       int resp_buftype = CIFS_NO_BUFFER;
-       __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
        struct TCP_Server_Info *server = ses->server;
-       u16 blob_length = 0;
-       struct key *spnego_key = NULL;
-       char *security_blob = NULL;
-       unsigned char *ntlmssp_blob = NULL;
-       bool use_spnego = false; /* else use raw ntlmssp */
-
-       cifs_dbg(FYI, "Session Setup\n");
-
-       if (!server) {
-               WARN(1, "%s: server is NULL!\n", __func__);
-               return -EIO;
-       }
-
-       /*
-        * If we are here due to reconnect, free per-smb session key
-        * in case signing was required.
-        */
-       kfree(ses->auth_key.response);
-       ses->auth_key.response = NULL;
-
-       /*
-        * If memory allocation is successful, caller of this function
-        * frees it.
-        */
-       ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
-       if (!ses->ntlmssp)
-               return -ENOMEM;
-       ses->ntlmssp->sesskey_per_smbsess = true;
-
-       /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
-       if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
-               ses->sectype = RawNTLMSSP;
-
-ssetup_ntlmssp_authenticate:
-       if (phase == NtLmChallenge)
-               phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
 
        rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req);
        if (rc)
                return rc;
 
        req->hdr.SessionId = 0; /* First session, not a reauthenticate */
+
+       /* if reconnect, we need to send previous sess id, otherwise it is 0 */
+       req->PreviousSessionId = sess_data->previous_session;
+
        req->Flags = 0; /* MBZ */
        /* to enable echos and oplocks */
        req->hdr.CreditRequest = cpu_to_le16(3);
@@ -642,199 +644,368 @@ ssetup_ntlmssp_authenticate:
        req->Capabilities = 0;
        req->Channel = 0; /* MBZ */
 
-       iov[0].iov_base = (char *)req;
+       sess_data->iov[0].iov_base = (char *)req;
        /* 4 for rfc1002 length field and 1 for pad */
-       iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
+       sess_data->iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
+       /*
+        * This variable will be used to clear the buffer
+        * allocated above in case of any error in the calling function.
+        */
+       sess_data->buf0_type = CIFS_SMALL_BUFFER;
 
-       if (ses->sectype == Kerberos) {
-#ifdef CONFIG_CIFS_UPCALL
-               struct cifs_spnego_msg *msg;
+       return 0;
+}
 
-               spnego_key = cifs_get_spnego_key(ses);
-               if (IS_ERR(spnego_key)) {
-                       rc = PTR_ERR(spnego_key);
-                       spnego_key = NULL;
-                       goto ssetup_exit;
-               }
+static void
+SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data)
+{
+       free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base);
+       sess_data->buf0_type = CIFS_NO_BUFFER;
+}
 
-               msg = spnego_key->payload.data[0];
-               /*
-                * check version field to make sure that cifs.upcall is
-                * sending us a response in an expected form
-                */
-               if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
-                       cifs_dbg(VFS,
-                                 "bad cifs.upcall version. Expected %d got %d",
-                                 CIFS_SPNEGO_UPCALL_VERSION, msg->version);
-                       rc = -EKEYREJECTED;
-                       goto ssetup_exit;
-               }
-               ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
-                                                GFP_KERNEL);
-               if (!ses->auth_key.response) {
-                       cifs_dbg(VFS,
-                               "Kerberos can't allocate (%u bytes) memory",
-                               msg->sesskey_len);
-                       rc = -ENOMEM;
-                       goto ssetup_exit;
-               }
-               ses->auth_key.len = msg->sesskey_len;
-               blob_length = msg->secblob_len;
-               iov[1].iov_base = msg->data + msg->sesskey_len;
-               iov[1].iov_len = blob_length;
-#else
-               rc = -EOPNOTSUPP;
-               goto ssetup_exit;
-#endif /* CONFIG_CIFS_UPCALL */
-       } else if (phase == NtLmNegotiate) { /* if not krb5 must be ntlmssp */
-               ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
-                                      GFP_KERNEL);
-               if (ntlmssp_blob == NULL) {
-                       rc = -ENOMEM;
-                       goto ssetup_exit;
-               }
-               build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
-               if (use_spnego) {
-                       /* blob_length = build_spnego_ntlmssp_blob(
-                                       &security_blob,
-                                       sizeof(struct _NEGOTIATE_MESSAGE),
-                                       ntlmssp_blob); */
-                       /* BB eventually need to add this */
-                       cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
-                       rc = -EOPNOTSUPP;
-                       kfree(ntlmssp_blob);
-                       goto ssetup_exit;
-               } else {
-                       blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
-                       /* with raw NTLMSSP we don't encapsulate in SPNEGO */
-                       security_blob = ntlmssp_blob;
-               }
-               iov[1].iov_base = security_blob;
-               iov[1].iov_len = blob_length;
-       } else if (phase == NtLmAuthenticate) {
-               req->hdr.SessionId = ses->Suid;
-               rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
-                                            nls_cp);
-               if (rc) {
-                       cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n",
-                                rc);
-                       goto ssetup_exit; /* BB double check error handling */
-               }
-               if (use_spnego) {
-                       /* blob_length = build_spnego_ntlmssp_blob(
-                                                       &security_blob,
-                                                       blob_length,
-                                                       ntlmssp_blob); */
-                       cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
-                       rc = -EOPNOTSUPP;
-                       kfree(ntlmssp_blob);
-                       goto ssetup_exit;
-               } else {
-                       security_blob = ntlmssp_blob;
-               }
-               iov[1].iov_base = security_blob;
-               iov[1].iov_len = blob_length;
-       } else {
-               cifs_dbg(VFS, "illegal ntlmssp phase\n");
-               rc = -EIO;
-               goto ssetup_exit;
-       }
+static int
+SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
+{
+       int rc;
+       struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base;
 
        /* Testing shows that buffer offset must be at location of Buffer[0] */
        req->SecurityBufferOffset =
-                               cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
-                                           1 /* pad */ - 4 /* rfc1001 len */);
-       req->SecurityBufferLength = cpu_to_le16(blob_length);
+               cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
+                       1 /* pad */ - 4 /* rfc1001 len */);
+       req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len);
 
-       inc_rfc1001_len(req, blob_length - 1 /* pad */);
+       inc_rfc1001_len(req, sess_data->iov[1].iov_len - 1 /* pad */);
 
        /* BB add code to build os and lm fields */
 
-       rc = SendReceive2(xid, ses, iov, 2, &resp_buftype,
-                         CIFS_LOG_ERROR | CIFS_NEG_OP);
+       rc = SendReceive2(sess_data->xid, sess_data->ses,
+                               sess_data->iov, 2,
+                               &sess_data->buf0_type,
+                               CIFS_LOG_ERROR | CIFS_NEG_OP);
 
-       kfree(security_blob);
-       rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
-       ses->Suid = rsp->hdr.SessionId;
-       if (resp_buftype != CIFS_NO_BUFFER &&
-           rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
-               if (phase != NtLmNegotiate) {
-                       cifs_dbg(VFS, "Unexpected more processing error\n");
-                       goto ssetup_exit;
-               }
-               if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
-                               le16_to_cpu(rsp->SecurityBufferOffset)) {
-                       cifs_dbg(VFS, "Invalid security buffer offset %d\n",
-                                le16_to_cpu(rsp->SecurityBufferOffset));
-                       rc = -EIO;
-                       goto ssetup_exit;
+       return rc;
+}
+
+static int
+SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
+{
+       int rc = 0;
+       struct cifs_ses *ses = sess_data->ses;
+
+       mutex_lock(&ses->server->srv_mutex);
+       if (ses->server->sign && ses->server->ops->generate_signingkey) {
+               rc = ses->server->ops->generate_signingkey(ses);
+               kfree(ses->auth_key.response);
+               ses->auth_key.response = NULL;
+               if (rc) {
+                       cifs_dbg(FYI,
+                               "SMB3 session key generation failed\n");
+                       mutex_unlock(&ses->server->srv_mutex);
+                       goto keygen_exit;
                }
+       }
+       if (!ses->server->session_estab) {
+               ses->server->sequence_number = 0x2;
+               ses->server->session_estab = true;
+       }
+       mutex_unlock(&ses->server->srv_mutex);
+
+       cifs_dbg(FYI, "SMB2/3 session established successfully\n");
+       spin_lock(&GlobalMid_Lock);
+       ses->status = CifsGood;
+       ses->need_reconnect = false;
+       spin_unlock(&GlobalMid_Lock);
 
-               /* NTLMSSP Negotiate sent now processing challenge (response) */
-               phase = NtLmChallenge; /* process ntlmssp challenge */
-               rc = 0; /* MORE_PROCESSING is not an error here but expected */
-               rc = decode_ntlmssp_challenge(rsp->Buffer,
-                               le16_to_cpu(rsp->SecurityBufferLength), ses);
+keygen_exit:
+       if (!ses->server->sign) {
+               kfree(ses->auth_key.response);
+               ses->auth_key.response = NULL;
+       }
+       return rc;
+}
+
+#ifdef CONFIG_CIFS_UPCALL
+static void
+SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
+{
+       int rc;
+       struct cifs_ses *ses = sess_data->ses;
+       struct cifs_spnego_msg *msg;
+       struct key *spnego_key = NULL;
+       struct smb2_sess_setup_rsp *rsp = NULL;
+
+       rc = SMB2_sess_alloc_buffer(sess_data);
+       if (rc)
+               goto out;
+
+       spnego_key = cifs_get_spnego_key(ses);
+       if (IS_ERR(spnego_key)) {
+               rc = PTR_ERR(spnego_key);
+               spnego_key = NULL;
+               goto out;
        }
 
+       msg = spnego_key->payload.data[0];
        /*
-        * BB eventually add code for SPNEGO decoding of NtlmChallenge blob,
-        * but at least the raw NTLMSSP case works.
+        * check version field to make sure that cifs.upcall is
+        * sending us a response in an expected form
         */
+       if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
+               cifs_dbg(VFS,
+                         "bad cifs.upcall version. Expected %d got %d",
+                         CIFS_SPNEGO_UPCALL_VERSION, msg->version);
+               rc = -EKEYREJECTED;
+               goto out_put_spnego_key;
+       }
+
+       ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
+                                        GFP_KERNEL);
+       if (!ses->auth_key.response) {
+               cifs_dbg(VFS,
+                       "Kerberos can't allocate (%u bytes) memory",
+                       msg->sesskey_len);
+               rc = -ENOMEM;
+               goto out_put_spnego_key;
+       }
+       ses->auth_key.len = msg->sesskey_len;
+
+       sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
+       sess_data->iov[1].iov_len = msg->secblob_len;
+
+       rc = SMB2_sess_sendreceive(sess_data);
+       if (rc)
+               goto out_put_spnego_key;
+
+       rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
+       ses->Suid = rsp->hdr.SessionId;
+
+       ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+       if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
+               cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
+
+       rc = SMB2_sess_establish_session(sess_data);
+out_put_spnego_key:
+       key_invalidate(spnego_key);
+       key_put(spnego_key);
+out:
+       sess_data->result = rc;
+       sess_data->func = NULL;
+       SMB2_sess_free_buffer(sess_data);
+}
+#else
+static void
+SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
+{
+       cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
+       sess_data->result = -EOPNOTSUPP;
+       sess_data->func = NULL;
+}
+#endif
+
+static void
+SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data);
+
+static void
+SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
+{
+       int rc;
+       struct cifs_ses *ses = sess_data->ses;
+       struct smb2_sess_setup_rsp *rsp = NULL;
+       char *ntlmssp_blob = NULL;
+       bool use_spnego = false; /* else use raw ntlmssp */
+       u16 blob_length = 0;
+
        /*
-        * No tcon so can't do
-        * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
+        * If memory allocation is successful, caller of this function
+        * frees it.
         */
-       if (rc != 0)
-               goto ssetup_exit;
+       ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
+       if (!ses->ntlmssp) {
+               rc = -ENOMEM;
+               goto out_err;
+       }
+       ses->ntlmssp->sesskey_per_smbsess = true;
+
+       rc = SMB2_sess_alloc_buffer(sess_data);
+       if (rc)
+               goto out_err;
+
+       ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
+                              GFP_KERNEL);
+       if (ntlmssp_blob == NULL) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
+       if (use_spnego) {
+               /* BB eventually need to add this */
+               cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
+               rc = -EOPNOTSUPP;
+               goto out;
+       } else {
+               blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
+               /* with raw NTLMSSP we don't encapsulate in SPNEGO */
+       }
+       sess_data->iov[1].iov_base = ntlmssp_blob;
+       sess_data->iov[1].iov_len = blob_length;
+
+       rc = SMB2_sess_sendreceive(sess_data);
+       rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
+
+       /* If true, rc here is expected and not an error */
+       if (sess_data->buf0_type != CIFS_NO_BUFFER &&
+               rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED)
+               rc = 0;
+
+       if (rc)
+               goto out;
+
+       if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
+                       le16_to_cpu(rsp->SecurityBufferOffset)) {
+               cifs_dbg(VFS, "Invalid security buffer offset %d\n",
+                       le16_to_cpu(rsp->SecurityBufferOffset));
+               rc = -EIO;
+               goto out;
+       }
+       rc = decode_ntlmssp_challenge(rsp->Buffer,
+                       le16_to_cpu(rsp->SecurityBufferLength), ses);
+       if (rc)
+               goto out;
+
+       cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
+
 
+       ses->Suid = rsp->hdr.SessionId;
        ses->session_flags = le16_to_cpu(rsp->SessionFlags);
        if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
                cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
-ssetup_exit:
-       free_rsp_buf(resp_buftype, rsp);
-
-       /* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
-       if ((phase == NtLmChallenge) && (rc == 0))
-               goto ssetup_ntlmssp_authenticate;
 
+out:
+       kfree(ntlmssp_blob);
+       SMB2_sess_free_buffer(sess_data);
        if (!rc) {
-               mutex_lock(&server->srv_mutex);
-               if (server->sign && server->ops->generate_signingkey) {
-                       rc = server->ops->generate_signingkey(ses);
-                       kfree(ses->auth_key.response);
-                       ses->auth_key.response = NULL;
-                       if (rc) {
-                               cifs_dbg(FYI,
-                                       "SMB3 session key generation failed\n");
-                               mutex_unlock(&server->srv_mutex);
-                               goto keygen_exit;
-                       }
-               }
-               if (!server->session_estab) {
-                       server->sequence_number = 0x2;
-                       server->session_estab = true;
-               }
-               mutex_unlock(&server->srv_mutex);
-
-               cifs_dbg(FYI, "SMB2/3 session established successfully\n");
-               spin_lock(&GlobalMid_Lock);
-               ses->status = CifsGood;
-               ses->need_reconnect = false;
-               spin_unlock(&GlobalMid_Lock);
+               sess_data->result = 0;
+               sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate;
+               return;
        }
+out_err:
+       kfree(ses->ntlmssp);
+       ses->ntlmssp = NULL;
+       sess_data->result = rc;
+       sess_data->func = NULL;
+}
 
-keygen_exit:
-       if (!server->sign) {
-               kfree(ses->auth_key.response);
-               ses->auth_key.response = NULL;
+static void
+SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
+{
+       int rc;
+       struct cifs_ses *ses = sess_data->ses;
+       struct smb2_sess_setup_req *req;
+       struct smb2_sess_setup_rsp *rsp = NULL;
+       unsigned char *ntlmssp_blob = NULL;
+       bool use_spnego = false; /* else use raw ntlmssp */
+       u16 blob_length = 0;
+
+       rc = SMB2_sess_alloc_buffer(sess_data);
+       if (rc)
+               goto out;
+
+       req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base;
+       req->hdr.SessionId = ses->Suid;
+
+       rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
+                                       sess_data->nls_cp);
+       if (rc) {
+               cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc);
+               goto out;
        }
-       if (spnego_key) {
-               key_invalidate(spnego_key);
-               key_put(spnego_key);
+
+       if (use_spnego) {
+               /* BB eventually need to add this */
+               cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
+               rc = -EOPNOTSUPP;
+               goto out;
        }
+       sess_data->iov[1].iov_base = ntlmssp_blob;
+       sess_data->iov[1].iov_len = blob_length;
+
+       rc = SMB2_sess_sendreceive(sess_data);
+       if (rc)
+               goto out;
+
+       rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
+
+       ses->Suid = rsp->hdr.SessionId;
+       ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+       if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
+               cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
+
+       rc = SMB2_sess_establish_session(sess_data);
+out:
+       kfree(ntlmssp_blob);
+       SMB2_sess_free_buffer(sess_data);
        kfree(ses->ntlmssp);
+       ses->ntlmssp = NULL;
+       sess_data->result = rc;
+       sess_data->func = NULL;
+}
 
+static int
+SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
+{
+       if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
+               ses->sectype = RawNTLMSSP;
+
+       switch (ses->sectype) {
+       case Kerberos:
+               sess_data->func = SMB2_auth_kerberos;
+               break;
+       case RawNTLMSSP:
+               sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate;
+               break;
+       default:
+               cifs_dbg(VFS, "secType %d not supported!\n", ses->sectype);
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+int
+SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
+               const struct nls_table *nls_cp)
+{
+       int rc = 0;
+       struct TCP_Server_Info *server = ses->server;
+       struct SMB2_sess_data *sess_data;
+
+       cifs_dbg(FYI, "Session Setup\n");
+
+       if (!server) {
+               WARN(1, "%s: server is NULL!\n", __func__);
+               return -EIO;
+       }
+
+       sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL);
+       if (!sess_data)
+               return -ENOMEM;
+
+       rc = SMB2_select_sec(ses, sess_data);
+       if (rc)
+               goto out;
+       sess_data->xid = xid;
+       sess_data->ses = ses;
+       sess_data->buf0_type = CIFS_NO_BUFFER;
+       sess_data->nls_cp = (struct nls_table *) nls_cp;
+
+       while (sess_data->func)
+               sess_data->func(sess_data);
+
+       rc = sess_data->result;
+out:
+       kfree(sess_data);
        return rc;
 }
 
@@ -1164,7 +1335,7 @@ create_durable_v2_buf(struct cifs_fid *pfid)
 
        buf->dcontext.Timeout = 0; /* Should this be configurable by workload */
        buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
-       get_random_bytes(buf->dcontext.CreateGuid, 16);
+       generate_random_uuid(buf->dcontext.CreateGuid);
        memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
 
        /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
@@ -2057,6 +2228,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
        if (rdata->credits) {
                buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
                                                SMB2_MAX_BUFFER_SIZE));
+               buf->CreditRequest = buf->CreditCharge;
                spin_lock(&server->req_lock);
                server->credits += rdata->credits -
                                                le16_to_cpu(buf->CreditCharge);
@@ -2243,6 +2415,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
        if (wdata->credits) {
                req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
                                                    SMB2_MAX_BUFFER_SIZE));
+               req->hdr.CreditRequest = req->hdr.CreditCharge;
                spin_lock(&server->req_lock);
                server->credits += wdata->credits -
                                        le16_to_cpu(req->hdr.CreditCharge);
index ff88d9feb01e7475f75a230c004d5f40a80f14f9..fd3709e8de33eddee8c66cca29cb6653921b020e 100644 (file)
@@ -276,7 +276,7 @@ struct smb2_sess_setup_req {
        __le32 Channel;
        __le16 SecurityBufferOffset;
        __le16 SecurityBufferLength;
-       __le64 PreviousSessionId;
+       __u64 PreviousSessionId;
        __u8   Buffer[1];       /* variable length GSS security buffer */
 } __packed;
 
index 5e23f64c0804ba647017cfa82808c4da85ff4760..20af5187ba63cc14150185952a3f7cdf9526556d 100644 (file)
@@ -33,7 +33,8 @@
 
 #define MAX_EA_VALUE_SIZE 65535
 #define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
-
+#define CIFS_XATTR_ATTRIB "cifs.dosattrib"  /* full name: user.cifs.dosattrib */
+#define CIFS_XATTR_CREATETIME "cifs.creationtime"  /* user.cifs.creationtime */
 /* BB need to add server (Samba e.g) support for security and trusted prefix */
 
 enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT };
@@ -144,6 +145,54 @@ out:
        return rc;
 }
 
+static int cifs_attrib_get(struct dentry *dentry,
+                          struct inode *inode, void *value,
+                          size_t size)
+{
+       ssize_t rc;
+       __u32 *pattribute;
+
+       rc = cifs_revalidate_dentry_attr(dentry);
+
+       if (rc)
+               return rc;
+
+       if ((value == NULL) || (size == 0))
+               return sizeof(__u32);
+       else if (size < sizeof(__u32))
+               return -ERANGE;
+
+       /* return dos attributes as pseudo xattr */
+       pattribute = (__u32 *)value;
+       *pattribute = CIFS_I(inode)->cifsAttrs;
+
+       return sizeof(__u32);
+}
+
+static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode,
+                                 void *value, size_t size)
+{
+       ssize_t rc;
+       __u64 * pcreatetime;
+
+       rc = cifs_revalidate_dentry_attr(dentry);
+       if (rc)
+               return rc;
+
+       if ((value == NULL) || (size == 0))
+               return sizeof(__u64);
+       else if (size < sizeof(__u64))
+               return -ERANGE;
+
+       /* return dos attributes as pseudo xattr */
+       pcreatetime = (__u64 *)value;
+       *pcreatetime = CIFS_I(inode)->createtime;
+       return sizeof(__u64);
+
+       return rc;
+}
+
+
 static int cifs_xattr_get(const struct xattr_handler *handler,
                          struct dentry *dentry, struct inode *inode,
                          const char *name, void *value, size_t size)
@@ -168,10 +217,19 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
                rc = -ENOMEM;
                goto out;
        }
-       /* return dos attributes as pseudo xattr */
+
        /* return alt name if available as pseudo attr */
        switch (handler->flags) {
        case XATTR_USER:
+               cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name);
+               if (strcmp(name, CIFS_XATTR_ATTRIB) == 0) {
+                       rc = cifs_attrib_get(dentry, inode, value, size);
+                       break;
+               } else if (strcmp(name, CIFS_XATTR_CREATETIME) == 0) {
+                       rc = cifs_creation_time_get(dentry, inode, value, size);
+                       break;
+               }
+
                if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
                        goto out;
 
index c1e9f29c924cd58d2f860d9b7a28f7c12e829d43..f2d7402abe02a59c95c4ef3ee1e556f9eea638b2 100644 (file)
@@ -1209,6 +1209,8 @@ COMPATIBLE_IOCTL(WDIOC_SETOPTIONS)
 COMPATIBLE_IOCTL(WDIOC_KEEPALIVE)
 COMPATIBLE_IOCTL(WDIOC_SETTIMEOUT)
 COMPATIBLE_IOCTL(WDIOC_GETTIMEOUT)
+COMPATIBLE_IOCTL(WDIOC_SETPRETIMEOUT)
+COMPATIBLE_IOCTL(WDIOC_GETPRETIMEOUT)
 /* Big R */
 COMPATIBLE_IOCTL(RNDGETENTCNT)
 COMPATIBLE_IOCTL(RNDADDTOENTCNT)
index 6fcfb3f7b137951b133d3db95431c23bc2d4677f..4e497b9ee71ee96d0647721e4649feff7adaf7c1 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -191,6 +191,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
 {
        struct page *page;
        int ret;
+       unsigned int gup_flags = FOLL_FORCE;
 
 #ifdef CONFIG_STACK_GROWSUP
        if (write) {
@@ -199,12 +200,16 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
                        return NULL;
        }
 #endif
+
+       if (write)
+               gup_flags |= FOLL_WRITE;
+
        /*
         * We are doing an exec().  'current' is the process
         * doing the exec and bprm->mm is the new process's mm.
         */
-       ret = get_user_pages_remote(current, bprm->mm, pos, 1, write,
-                       1, &page, NULL);
+       ret = get_user_pages_remote(current, bprm->mm, pos, 1, gup_flags,
+                       &page, NULL);
        if (ret <= 0)
                return NULL;
 
index 207ba8d627ca74feca42b4196df15ca0a9a621e6..a4b531be9168d576e7e5f875bf5af6dc9a01020c 100644 (file)
@@ -428,10 +428,10 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
        if (!nop || !nop->fh_to_dentry)
                return ERR_PTR(-ESTALE);
        result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
-       if (!result)
-               result = ERR_PTR(-ESTALE);
-       if (IS_ERR(result))
-               return result;
+       if (PTR_ERR(result) == -ENOMEM)
+               return ERR_CAST(result);
+       if (IS_ERR_OR_NULL(result))
+               return ERR_PTR(-ESTALE);
 
        if (d_is_dir(result)) {
                /*
@@ -541,6 +541,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
 
  err_result:
        dput(result);
+       if (err != -ENOMEM)
+               err = -ESTALE;
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(exportfs_decode_fh);
index 93985c64d8a8bef1328ddd5f35cc7d047f41fc3a..6f14ee923acd2b4cf75f93bed945f6fc04b79c93 100644 (file)
@@ -852,16 +852,16 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
 
        for (segno = start_segno; segno < end_segno; segno++) {
 
-               if (get_valid_blocks(sbi, segno, 1) == 0 ||
-                                       unlikely(f2fs_cp_error(sbi)))
-                       goto next;
-
                /* find segment summary of victim */
                sum_page = find_get_page(META_MAPPING(sbi),
                                        GET_SUM_BLOCK(sbi, segno));
-               f2fs_bug_on(sbi, !PageUptodate(sum_page));
                f2fs_put_page(sum_page, 0);
 
+               if (get_valid_blocks(sbi, segno, 1) == 0 ||
+                               !PageUptodate(sum_page) ||
+                               unlikely(f2fs_cp_error(sbi)))
+                       goto next;
+
                sum = page_address(sum_page);
                f2fs_bug_on(sbi, type != GET_SUM_TYPE((&sum->footer)));
 
index dcd96aac02f5c401451479dfec78d7db87c45bae..cf4c636ff4da5ab2d345261fc3d7e4f0db6d4c3c 100644 (file)
@@ -110,8 +110,9 @@ static struct kernfs_node *kernfs_common_ancestor(struct kernfs_node *a,
  * kn_to:   /n1/n2/n3         [depth=3]
  * result:  /../..
  *
- * return value: length of the string.  If greater than buflen,
- * then contents of buf are undefined.  On error, -1 is returned.
+ * Returns the length of the full path.  If the full length is equal to or
+ * greater than @buflen, @buf contains the truncated path with the trailing
+ * '\0'.  On error, -errno is returned.
  */
 static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
                                        struct kernfs_node *kn_from,
@@ -119,9 +120,8 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
 {
        struct kernfs_node *kn, *common;
        const char parent_str[] = "/..";
-       size_t depth_from, depth_to, len = 0, nlen = 0;
-       char *p;
-       int i;
+       size_t depth_from, depth_to, len = 0;
+       int i, j;
 
        if (!kn_from)
                kn_from = kernfs_root(kn_to)->kn;
@@ -131,7 +131,7 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
 
        common = kernfs_common_ancestor(kn_from, kn_to);
        if (WARN_ON(!common))
-               return -1;
+               return -EINVAL;
 
        depth_to = kernfs_depth(common, kn_to);
        depth_from = kernfs_depth(common, kn_from);
@@ -144,22 +144,16 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
                               len < buflen ? buflen - len : 0);
 
        /* Calculate how many bytes we need for the rest */
-       for (kn = kn_to; kn != common; kn = kn->parent)
-               nlen += strlen(kn->name) + 1;
-
-       if (len + nlen >= buflen)
-               return len + nlen;
-
-       p = buf + len + nlen;
-       *p = '\0';
-       for (kn = kn_to; kn != common; kn = kn->parent) {
-               size_t tmp = strlen(kn->name);
-               p -= tmp;
-               memcpy(p, kn->name, tmp);
-               *(--p) = '/';
+       for (i = depth_to - 1; i >= 0; i--) {
+               for (kn = kn_to, j = 0; j < i; j++)
+                       kn = kn->parent;
+               len += strlcpy(buf + len, "/",
+                              len < buflen ? buflen - len : 0);
+               len += strlcpy(buf + len, kn->name,
+                              len < buflen ? buflen - len : 0);
        }
 
-       return len + nlen;
+       return len;
 }
 
 /**
@@ -185,29 +179,6 @@ int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
        return ret;
 }
 
-/**
- * kernfs_path_len - determine the length of the full path of a given node
- * @kn: kernfs_node of interest
- *
- * The returned length doesn't include the space for the terminating '\0'.
- */
-size_t kernfs_path_len(struct kernfs_node *kn)
-{
-       size_t len = 0;
-       unsigned long flags;
-
-       spin_lock_irqsave(&kernfs_rename_lock, flags);
-
-       do {
-               len += strlen(kn->name) + 1;
-               kn = kn->parent;
-       } while (kn && kn->parent);
-
-       spin_unlock_irqrestore(&kernfs_rename_lock, flags);
-
-       return len;
-}
-
 /**
  * kernfs_path_from_node - build path of node @to relative to @from.
  * @from: parent kernfs_node relative to which we need to build the path
@@ -220,8 +191,9 @@ size_t kernfs_path_len(struct kernfs_node *kn)
  * path (which includes '..'s) as needed to reach from @from to @to is
  * returned.
  *
- * If @buf isn't long enough, the return value will be greater than @buflen
- * and @buf contents are undefined.
+ * Returns the length of the full path.  If the full length is equal to or
+ * greater than @buflen, @buf contains the truncated path with the trailing
+ * '\0'.  On error, -errno is returned.
  */
 int kernfs_path_from_node(struct kernfs_node *to, struct kernfs_node *from,
                          char *buf, size_t buflen)
@@ -236,28 +208,6 @@ int kernfs_path_from_node(struct kernfs_node *to, struct kernfs_node *from,
 }
 EXPORT_SYMBOL_GPL(kernfs_path_from_node);
 
-/**
- * kernfs_path - build full path of a given node
- * @kn: kernfs_node of interest
- * @buf: buffer to copy @kn's name into
- * @buflen: size of @buf
- *
- * Builds and returns the full path of @kn in @buf of @buflen bytes.  The
- * path is built from the end of @buf so the returned pointer usually
- * doesn't match @buf.  If @buf isn't long enough, @buf is nul terminated
- * and %NULL is returned.
- */
-char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen)
-{
-       int ret;
-
-       ret = kernfs_path_from_node(kn, NULL, buf, buflen);
-       if (ret < 0 || ret >= buflen)
-               return NULL;
-       return buf;
-}
-EXPORT_SYMBOL_GPL(kernfs_path);
-
 /**
  * pr_cont_kernfs_name - pr_cont name of a kernfs_node
  * @kn: kernfs_node of interest
index ce93b416b490fa558589a6bdd2bb620ffb108fcc..22c5b4aa49611ac46cb50dbf4fc8ef25ec500b34 100644 (file)
@@ -1609,6 +1609,7 @@ int fcntl_getlease(struct file *filp)
 
        ctx = smp_load_acquire(&inode->i_flctx);
        if (ctx && !list_empty_careful(&ctx->flc_lease)) {
+               percpu_down_read_preempt_disable(&file_rwsem);
                spin_lock(&ctx->flc_lock);
                time_out_leases(inode, &dispose);
                list_for_each_entry(fl, &ctx->flc_lease, fl_list) {
@@ -1618,6 +1619,8 @@ int fcntl_getlease(struct file *filp)
                        break;
                }
                spin_unlock(&ctx->flc_lock);
+               percpu_up_read_preempt_enable(&file_rwsem);
+
                locks_dispose_list(&dispose);
        }
        return type;
@@ -2529,11 +2532,14 @@ locks_remove_lease(struct file *filp, struct file_lock_context *ctx)
        if (list_empty(&ctx->flc_lease))
                return;
 
+       percpu_down_read_preempt_disable(&file_rwsem);
        spin_lock(&ctx->flc_lock);
        list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, fl_list)
                if (filp == fl->fl_file)
                        lease_modify(fl, F_UNLCK, &dispose);
        spin_unlock(&ctx->flc_lock);
+       percpu_up_read_preempt_enable(&file_rwsem);
+
        locks_dispose_list(&dispose);
 }
 
index a7f601cd521a079af70d0364a67478ab3e9d5831..5b4eed2215304a14ac2614058ae2b2002a3f2ae9 100644 (file)
@@ -4668,6 +4668,31 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 }
 EXPORT_SYMBOL(generic_readlink);
 
+/**
+ * vfs_get_link - get symlink body
+ * @dentry: dentry on which to get symbolic link
+ * @done: caller needs to free returned data with this
+ *
+ * Calls security hook and i_op->get_link() on the supplied inode.
+ *
+ * It does not touch atime.  That's up to the caller if necessary.
+ *
+ * Does not work on "special" symlinks like /proc/$$/fd/N
+ */
+const char *vfs_get_link(struct dentry *dentry, struct delayed_call *done)
+{
+       const char *res = ERR_PTR(-EINVAL);
+       struct inode *inode = d_inode(dentry);
+
+       if (d_is_symlink(dentry)) {
+               res = ERR_PTR(security_inode_readlink(dentry));
+               if (!res)
+                       res = inode->i_op->get_link(dentry, inode, done);
+       }
+       return res;
+}
+EXPORT_SYMBOL(vfs_get_link);
+
 /* get the link contents into pagecache */
 const char *page_get_link(struct dentry *dentry, struct inode *inode,
                          struct delayed_call *callback)
index 58aca9c931acaecc62bf02717c9cfa9d58baf8cf..e6c234b1a6456c1364bcc06b029f3cc4a155d5bd 100644 (file)
@@ -2824,6 +2824,7 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)
        return new_ns;
 }
 
+__latent_entropy
 struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
                struct user_namespace *user_ns, struct fs_struct *new_fs)
 {
index 5f7b053720eed9741250193784f0c33ac378df69..6de15709d02497642eaefc4940f43064d820676a 100644 (file)
@@ -76,7 +76,7 @@ static void nfs_dns_cache_revisit(struct cache_deferred_req *d, int toomany)
 
        dreq = container_of(d, struct nfs_cache_defer_req, deferred_req);
 
-       complete_all(&dreq->completion);
+       complete(&dreq->completion);
        nfs_cache_defer_req_put(dreq);
 }
 
index 52a28311e2a4b2d72063cf36ac9e4296481211b7..532d8e242d4d76c44413d6c814c7548094614ebe 100644 (file)
@@ -31,8 +31,6 @@
 struct nfs_callback_data {
        unsigned int users;
        struct svc_serv *serv;
-       struct svc_rqst *rqst;
-       struct task_struct *task;
 };
 
 static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
@@ -89,15 +87,6 @@ nfs4_callback_svc(void *vrqstp)
        return 0;
 }
 
-/*
- * Prepare to bring up the NFSv4 callback service
- */
-static struct svc_rqst *
-nfs4_callback_up(struct svc_serv *serv)
-{
-       return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
-}
-
 #if defined(CONFIG_NFS_V4_1)
 /*
  * The callback service for NFSv4.1 callbacks
@@ -139,29 +128,6 @@ nfs41_callback_svc(void *vrqstp)
        return 0;
 }
 
-/*
- * Bring up the NFSv4.1 callback service
- */
-static struct svc_rqst *
-nfs41_callback_up(struct svc_serv *serv)
-{
-       struct svc_rqst *rqstp;
-
-       INIT_LIST_HEAD(&serv->sv_cb_list);
-       spin_lock_init(&serv->sv_cb_lock);
-       init_waitqueue_head(&serv->sv_cb_waitq);
-       rqstp = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
-       dprintk("--> %s return %d\n", __func__, PTR_ERR_OR_ZERO(rqstp));
-       return rqstp;
-}
-
-static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
-               struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
-{
-       *rqstpp = nfs41_callback_up(serv);
-       *callback_svc = nfs41_callback_svc;
-}
-
 static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
                struct svc_serv *serv)
 {
@@ -173,13 +139,6 @@ static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
                xprt->bc_serv = serv;
 }
 #else
-static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
-               struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
-{
-       *rqstpp = ERR_PTR(-ENOTSUPP);
-       *callback_svc = ERR_PTR(-ENOTSUPP);
-}
-
 static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
                struct svc_serv *serv)
 {
@@ -189,45 +148,22 @@ static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
                                  struct svc_serv *serv)
 {
-       struct svc_rqst *rqstp;
-       int (*callback_svc)(void *vrqstp);
-       struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
+       int nrservs = nfs_callback_nr_threads;
        int ret;
 
        nfs_callback_bc_serv(minorversion, xprt, serv);
 
-       if (cb_info->task)
-               return 0;
+       if (nrservs < NFS4_MIN_NR_CALLBACK_THREADS)
+               nrservs = NFS4_MIN_NR_CALLBACK_THREADS;
 
-       switch (minorversion) {
-       case 0:
-               /* v4.0 callback setup */
-               rqstp = nfs4_callback_up(serv);
-               callback_svc = nfs4_callback_svc;
-               break;
-       default:
-               nfs_minorversion_callback_svc_setup(serv,
-                               &rqstp, &callback_svc);
-       }
-
-       if (IS_ERR(rqstp))
-               return PTR_ERR(rqstp);
-
-       svc_sock_update_bufs(serv);
+       if (serv->sv_nrthreads-1 == nrservs)
+               return 0;
 
-       cb_info->serv = serv;
-       cb_info->rqst = rqstp;
-       cb_info->task = kthread_create(callback_svc, cb_info->rqst,
-                                   "nfsv4.%u-svc", minorversion);
-       if (IS_ERR(cb_info->task)) {
-               ret = PTR_ERR(cb_info->task);
-               svc_exit_thread(cb_info->rqst);
-               cb_info->rqst = NULL;
-               cb_info->task = NULL;
+       ret = serv->sv_ops->svo_setup(serv, NULL, nrservs);
+       if (ret) {
+               serv->sv_ops->svo_setup(serv, NULL, 0);
                return ret;
        }
-       rqstp->rq_task = cb_info->task;
-       wake_up_process(cb_info->task);
        dprintk("nfs_callback_up: service started\n");
        return 0;
 }
@@ -281,19 +217,41 @@ err_bind:
        return ret;
 }
 
-static struct svc_serv_ops nfs_cb_sv_ops = {
+static struct svc_serv_ops nfs40_cb_sv_ops = {
+       .svo_function           = nfs4_callback_svc,
        .svo_enqueue_xprt       = svc_xprt_do_enqueue,
+       .svo_setup              = svc_set_num_threads,
+       .svo_module             = THIS_MODULE,
+};
+#if defined(CONFIG_NFS_V4_1)
+static struct svc_serv_ops nfs41_cb_sv_ops = {
+       .svo_function           = nfs41_callback_svc,
+       .svo_enqueue_xprt       = svc_xprt_do_enqueue,
+       .svo_setup              = svc_set_num_threads,
+       .svo_module             = THIS_MODULE,
+};
+
+struct svc_serv_ops *nfs4_cb_sv_ops[] = {
+       [0] = &nfs40_cb_sv_ops,
+       [1] = &nfs41_cb_sv_ops,
+};
+#else
+struct svc_serv_ops *nfs4_cb_sv_ops[] = {
+       [0] = &nfs40_cb_sv_ops,
+       [1] = NULL,
 };
+#endif
 
 static struct svc_serv *nfs_callback_create_svc(int minorversion)
 {
        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
        struct svc_serv *serv;
+       struct svc_serv_ops *sv_ops;
 
        /*
         * Check whether we're already up and running.
         */
-       if (cb_info->task) {
+       if (cb_info->serv) {
                /*
                 * Note: increase service usage, because later in case of error
                 * svc_destroy() will be called.
@@ -302,6 +260,17 @@ static struct svc_serv *nfs_callback_create_svc(int minorversion)
                return cb_info->serv;
        }
 
+       switch (minorversion) {
+       case 0:
+               sv_ops = nfs4_cb_sv_ops[0];
+               break;
+       default:
+               sv_ops = nfs4_cb_sv_ops[1];
+       }
+
+       if (sv_ops == NULL)
+               return ERR_PTR(-ENOTSUPP);
+
        /*
         * Sanity check: if there's no task,
         * we should be the first user ...
@@ -310,11 +279,12 @@ static struct svc_serv *nfs_callback_create_svc(int minorversion)
                printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
                        cb_info->users);
 
-       serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, &nfs_cb_sv_ops);
+       serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, sv_ops);
        if (!serv) {
                printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
                return ERR_PTR(-ENOMEM);
        }
+       cb_info->serv = serv;
        /* As there is only one thread we need to over-ride the
         * default maximum of 80 connections
         */
@@ -357,6 +327,8 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
         * thread exits.
         */
 err_net:
+       if (!cb_info->users)
+               cb_info->serv = NULL;
        svc_destroy(serv);
 err_create:
        mutex_unlock(&nfs_callback_mutex);
@@ -374,18 +346,18 @@ err_start:
 void nfs_callback_down(int minorversion, struct net *net)
 {
        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
+       struct svc_serv *serv;
 
        mutex_lock(&nfs_callback_mutex);
-       nfs_callback_down_net(minorversion, cb_info->serv, net);
+       serv = cb_info->serv;
+       nfs_callback_down_net(minorversion, serv, net);
        cb_info->users--;
-       if (cb_info->users == 0 && cb_info->task != NULL) {
-               kthread_stop(cb_info->task);
-               dprintk("nfs_callback_down: service stopped\n");
-               svc_exit_thread(cb_info->rqst);
+       if (cb_info->users == 0) {
+               svc_get(serv);
+               serv->sv_ops->svo_setup(serv, NULL, 0);
+               svc_destroy(serv);
                dprintk("nfs_callback_down: service destroyed\n");
                cb_info->serv = NULL;
-               cb_info->rqst = NULL;
-               cb_info->task = NULL;
        }
        mutex_unlock(&nfs_callback_mutex);
 }
index 5fe1cecbf9f033ce0603639e771c1d82520599b0..c701c308fac52b16dc87d4b357d0b84f0627aa11 100644 (file)
@@ -179,6 +179,15 @@ extern __be32 nfs4_callback_devicenotify(
        struct cb_devicenotifyargs *args,
        void *dummy, struct cb_process_state *cps);
 
+struct cb_notify_lock_args {
+       struct nfs_fh                   cbnl_fh;
+       struct nfs_lowner               cbnl_owner;
+       bool                            cbnl_valid;
+};
+
+extern __be32 nfs4_callback_notify_lock(struct cb_notify_lock_args *args,
+                                        void *dummy,
+                                        struct cb_process_state *cps);
 #endif /* CONFIG_NFS_V4_1 */
 extern int check_gss_callback_principal(struct nfs_client *, struct svc_rqst *);
 extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args,
@@ -198,6 +207,9 @@ extern void nfs_callback_down(int minorversion, struct net *net);
 #define NFS41_BC_MIN_CALLBACKS 1
 #define NFS41_BC_MAX_CALLBACKS 1
 
+#define NFS4_MIN_NR_CALLBACK_THREADS 1
+
 extern unsigned int nfs_callback_set_tcpport;
+extern unsigned short nfs_callback_nr_threads;
 
 #endif /* __LINUX_FS_NFS_CALLBACK_H */
index f953ef6b2f2e7eafcfb7fb9015972f12f214811e..e9aa235e9d10197d8b138f1d0e657dd6449c578f 100644 (file)
@@ -628,4 +628,20 @@ out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
        return status;
 }
+
+__be32 nfs4_callback_notify_lock(struct cb_notify_lock_args *args, void *dummy,
+                                struct cb_process_state *cps)
+{
+       if (!cps->clp) /* set in cb_sequence */
+               return htonl(NFS4ERR_OP_NOT_IN_SESSION);
+
+       dprintk_rcu("NFS: CB_NOTIFY_LOCK request from %s\n",
+               rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
+
+       /* Don't wake anybody if the string looked bogus */
+       if (args->cbnl_valid)
+               __wake_up(&cps->clp->cl_lock_waitq, TASK_NORMAL, 0, args);
+
+       return htonl(NFS4_OK);
+}
 #endif /* CONFIG_NFS_V4_1 */
index 656f68f7fe53e110a33f8bb876028e6083c3db20..eb094c6011d85bb7ce7bd544a3d203defd50b03a 100644 (file)
@@ -35,6 +35,7 @@
                                         (1 + 3) * 4) // seqid, 3 slotids
 #define CB_OP_RECALLANY_RES_MAXSZ      (CB_OP_HDR_RES_MAXSZ)
 #define CB_OP_RECALLSLOT_RES_MAXSZ     (CB_OP_HDR_RES_MAXSZ)
+#define CB_OP_NOTIFY_LOCK_RES_MAXSZ    (CB_OP_HDR_RES_MAXSZ)
 #endif /* CONFIG_NFS_V4_1 */
 
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
@@ -72,7 +73,7 @@ static int nfs4_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
        return xdr_ressize_check(rqstp, p);
 }
 
-static __be32 *read_buf(struct xdr_stream *xdr, int nbytes)
+static __be32 *read_buf(struct xdr_stream *xdr, size_t nbytes)
 {
        __be32 *p;
 
@@ -534,6 +535,49 @@ static __be32 decode_recallslot_args(struct svc_rqst *rqstp,
        return 0;
 }
 
+static __be32 decode_lockowner(struct xdr_stream *xdr, struct cb_notify_lock_args *args)
+{
+       __be32          *p;
+       unsigned int    len;
+
+       p = read_buf(xdr, 12);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_BADXDR);
+
+       p = xdr_decode_hyper(p, &args->cbnl_owner.clientid);
+       len = be32_to_cpu(*p);
+
+       p = read_buf(xdr, len);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_BADXDR);
+
+       /* Only try to decode if the length is right */
+       if (len == 20) {
+               p += 2; /* skip "lock id:" */
+               args->cbnl_owner.s_dev = be32_to_cpu(*p++);
+               xdr_decode_hyper(p, &args->cbnl_owner.id);
+               args->cbnl_valid = true;
+       } else {
+               args->cbnl_owner.s_dev = 0;
+               args->cbnl_owner.id = 0;
+               args->cbnl_valid = false;
+       }
+       return 0;
+}
+
+static __be32 decode_notify_lock_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_notify_lock_args *args)
+{
+       __be32 status;
+
+       status = decode_fh(xdr, &args->cbnl_fh);
+       if (unlikely(status != 0))
+               goto out;
+       status = decode_lockowner(xdr, args);
+out:
+       dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+       return status;
+}
+
 #endif /* CONFIG_NFS_V4_1 */
 
 static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
@@ -746,6 +790,7 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        case OP_CB_RECALL_SLOT:
        case OP_CB_LAYOUTRECALL:
        case OP_CB_NOTIFY_DEVICEID:
+       case OP_CB_NOTIFY_LOCK:
                *op = &callback_ops[op_nr];
                break;
 
@@ -753,7 +798,6 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        case OP_CB_PUSH_DELEG:
        case OP_CB_RECALLABLE_OBJ_AVAIL:
        case OP_CB_WANTS_CANCELLED:
-       case OP_CB_NOTIFY_LOCK:
                return htonl(NFS4ERR_NOTSUPP);
 
        default:
@@ -1006,6 +1050,11 @@ static struct callback_op callback_ops[] = {
                .decode_args = (callback_decode_arg_t)decode_recallslot_args,
                .res_maxsize = CB_OP_RECALLSLOT_RES_MAXSZ,
        },
+       [OP_CB_NOTIFY_LOCK] = {
+               .process_op = (callback_process_op_t)nfs4_callback_notify_lock,
+               .decode_args = (callback_decode_arg_t)decode_notify_lock_args,
+               .res_maxsize = CB_OP_NOTIFY_LOCK_RES_MAXSZ,
+       },
 #endif /* CONFIG_NFS_V4_1 */
 };
 
index 1e106780a23752fa9b49ed74baa9bd01c500c912..7555ba889d1fce916cc96b8f23c03ad4d6037366 100644 (file)
@@ -313,7 +313,10 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
                        continue;
                /* Match the full socket address */
                if (!rpc_cmp_addr_port(sap, clap))
-                       continue;
+                       /* Match all xprt_switch full socket addresses */
+                       if (!rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient,
+                                                          sap))
+                               continue;
 
                atomic_inc(&clp->cl_count);
                return clp;
@@ -785,7 +788,8 @@ int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs
        }
 
        fsinfo.fattr = fattr;
-       fsinfo.layouttype = 0;
+       fsinfo.nlayouttypes = 0;
+       memset(fsinfo.layouttype, 0, sizeof(fsinfo.layouttype));
        error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo);
        if (error < 0)
                goto out_error;
@@ -1078,7 +1082,7 @@ void nfs_clients_init(struct net *net)
        idr_init(&nn->cb_ident_idr);
 #endif
        spin_lock_init(&nn->nfs_client_lock);
-       nn->boot_time = CURRENT_TIME;
+       nn->boot_time = ktime_get_real();
 }
 
 #ifdef CONFIG_PROC_FS
index 322c2585bc341eb49d5045e0898101ae7a1abab9..dff600ae0d7477333a3e41749cffe58ad77c7e3a 100644 (file)
@@ -41,6 +41,17 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
        set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
 }
 
+static bool
+nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
+               fmode_t flags)
+{
+       if (delegation != NULL && (delegation->type & flags) == flags &&
+           !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
+           !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
+               return true;
+       return false;
+}
+
 static int
 nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
 {
@@ -50,8 +61,7 @@ nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
        flags &= FMODE_READ|FMODE_WRITE;
        rcu_read_lock();
        delegation = rcu_dereference(NFS_I(inode)->delegation);
-       if (delegation != NULL && (delegation->type & flags) == flags &&
-           !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
+       if (nfs4_is_valid_delegation(delegation, flags)) {
                if (mark)
                        nfs_mark_delegation_referenced(delegation);
                ret = 1;
@@ -185,15 +195,13 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
                        rcu_read_unlock();
                        put_rpccred(oldcred);
                        trace_nfs4_reclaim_delegation(inode, res->delegation_type);
-               } else {
-                       /* We appear to have raced with a delegation return. */
-                       spin_unlock(&delegation->lock);
-                       rcu_read_unlock();
-                       nfs_inode_set_delegation(inode, cred, res);
+                       return;
                }
-       } else {
-               rcu_read_unlock();
+               /* We appear to have raced with a delegation return. */
+               spin_unlock(&delegation->lock);
        }
+       rcu_read_unlock();
+       nfs_inode_set_delegation(inode, cred, res);
 }
 
 static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
@@ -642,28 +650,49 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
        rcu_read_unlock();
 }
 
-static void nfs_revoke_delegation(struct inode *inode)
+static void nfs_mark_delegation_revoked(struct nfs_server *server,
+               struct nfs_delegation *delegation)
+{
+       set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
+       delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
+       nfs_mark_return_delegation(server, delegation);
+}
+
+static bool nfs_revoke_delegation(struct inode *inode,
+               const nfs4_stateid *stateid)
 {
        struct nfs_delegation *delegation;
+       nfs4_stateid tmp;
+       bool ret = false;
+
        rcu_read_lock();
        delegation = rcu_dereference(NFS_I(inode)->delegation);
-       if (delegation != NULL) {
-               set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
-               nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
-       }
+       if (delegation == NULL)
+               goto out;
+       if (stateid == NULL) {
+               nfs4_stateid_copy(&tmp, &delegation->stateid);
+               stateid = &tmp;
+       } else if (!nfs4_stateid_match(stateid, &delegation->stateid))
+               goto out;
+       nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
+       ret = true;
+out:
        rcu_read_unlock();
+       if (ret)
+               nfs_inode_find_state_and_recover(inode, stateid);
+       return ret;
 }
 
-void nfs_remove_bad_delegation(struct inode *inode)
+void nfs_remove_bad_delegation(struct inode *inode,
+               const nfs4_stateid *stateid)
 {
        struct nfs_delegation *delegation;
 
-       nfs_revoke_delegation(inode);
+       if (!nfs_revoke_delegation(inode, stateid))
+               return;
        delegation = nfs_inode_detach_delegation(inode);
-       if (delegation) {
-               nfs_inode_find_state_and_recover(inode, &delegation->stateid);
+       if (delegation)
                nfs_free_delegation(delegation);
-       }
 }
 EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
 
@@ -786,8 +815,15 @@ static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
 {
        struct nfs_delegation *delegation;
 
-       list_for_each_entry_rcu(delegation, &server->delegations, super_list)
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
+               /*
+                * If the delegation may have been admin revoked, then we
+                * cannot reclaim it.
+                */
+               if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags))
+                       continue;
                set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
+       }
 }
 
 /**
@@ -851,6 +887,141 @@ restart:
        rcu_read_unlock();
 }
 
+static inline bool nfs4_server_rebooted(const struct nfs_client *clp)
+{
+       return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) |
+                               BIT(NFS4CLNT_LEASE_EXPIRED) |
+                               BIT(NFS4CLNT_SESSION_RESET))) != 0;
+}
+
+static void nfs_mark_test_expired_delegation(struct nfs_server *server,
+           struct nfs_delegation *delegation)
+{
+       if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE)
+               return;
+       clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
+       set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
+       set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state);
+}
+
+static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server,
+               struct inode *inode)
+{
+       struct nfs_delegation *delegation;
+
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation)
+               nfs_mark_test_expired_delegation(server, delegation);
+       rcu_read_unlock();
+
+}
+
+static void nfs_delegation_mark_test_expired_server(struct nfs_server *server)
+{
+       struct nfs_delegation *delegation;
+
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list)
+               nfs_mark_test_expired_delegation(server, delegation);
+}
+
+/**
+ * nfs_mark_test_expired_all_delegations - mark all delegations for testing
+ * @clp: nfs_client to process
+ *
+ * Iterates through all the delegations associated with this server and
+ * marks them as needing to be checked for validity.
+ */
+void nfs_mark_test_expired_all_delegations(struct nfs_client *clp)
+{
+       struct nfs_server *server;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+               nfs_delegation_mark_test_expired_server(server);
+       rcu_read_unlock();
+}
+
+/**
+ * nfs_reap_expired_delegations - reap expired delegations
+ * @clp: nfs_client to process
+ *
+ * Iterates through all the delegations associated with this server and
+ * checks if they have may have been revoked. This function is usually
+ * expected to be called in cases where the server may have lost its
+ * lease.
+ */
+void nfs_reap_expired_delegations(struct nfs_client *clp)
+{
+       const struct nfs4_minor_version_ops *ops = clp->cl_mvops;
+       struct nfs_delegation *delegation;
+       struct nfs_server *server;
+       struct inode *inode;
+       struct rpc_cred *cred;
+       nfs4_stateid stateid;
+
+restart:
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+               list_for_each_entry_rcu(delegation, &server->delegations,
+                                                               super_list) {
+                       if (test_bit(NFS_DELEGATION_RETURNING,
+                                               &delegation->flags))
+                               continue;
+                       if (test_bit(NFS_DELEGATION_TEST_EXPIRED,
+                                               &delegation->flags) == 0)
+                               continue;
+                       if (!nfs_sb_active(server->super))
+                               continue;
+                       inode = nfs_delegation_grab_inode(delegation);
+                       if (inode == NULL) {
+                               rcu_read_unlock();
+                               nfs_sb_deactive(server->super);
+                               goto restart;
+                       }
+                       cred = get_rpccred_rcu(delegation->cred);
+                       nfs4_stateid_copy(&stateid, &delegation->stateid);
+                       clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
+                       rcu_read_unlock();
+                       if (cred != NULL &&
+                           ops->test_and_free_expired(server, &stateid, cred) < 0) {
+                               nfs_revoke_delegation(inode, &stateid);
+                               nfs_inode_find_state_and_recover(inode, &stateid);
+                       }
+                       put_rpccred(cred);
+                       if (nfs4_server_rebooted(clp)) {
+                               nfs_inode_mark_test_expired_delegation(server,inode);
+                               iput(inode);
+                               nfs_sb_deactive(server->super);
+                               return;
+                       }
+                       iput(inode);
+                       nfs_sb_deactive(server->super);
+                       goto restart;
+               }
+       }
+       rcu_read_unlock();
+}
+
+void nfs_inode_find_delegation_state_and_recover(struct inode *inode,
+               const nfs4_stateid *stateid)
+{
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_delegation *delegation;
+       bool found = false;
+
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation &&
+           nfs4_stateid_match_other(&delegation->stateid, stateid)) {
+               nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation);
+               found = true;
+       }
+       rcu_read_unlock();
+       if (found)
+               nfs4_schedule_state_manager(clp);
+}
+
 /**
  * nfs_delegations_present - check for existence of delegations
  * @clp: client state handle
@@ -893,7 +1064,7 @@ bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags,
        flags &= FMODE_READ|FMODE_WRITE;
        rcu_read_lock();
        delegation = rcu_dereference(nfsi->delegation);
-       ret = (delegation != NULL && (delegation->type & flags) == flags);
+       ret = nfs4_is_valid_delegation(delegation, flags);
        if (ret) {
                nfs4_stateid_copy(dst, &delegation->stateid);
                nfs_mark_delegation_referenced(delegation);
index 64724d252a7973074fcff861a31164c9df7415f9..e9d55579687393552b0d83c46dfcafc56dbace89 100644 (file)
@@ -32,6 +32,7 @@ enum {
        NFS_DELEGATION_REFERENCED,
        NFS_DELEGATION_RETURNING,
        NFS_DELEGATION_REVOKED,
+       NFS_DELEGATION_TEST_EXPIRED,
 };
 
 int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
@@ -47,11 +48,14 @@ void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags);
 void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
 int nfs_client_return_marked_delegations(struct nfs_client *clp);
 int nfs_delegations_present(struct nfs_client *clp);
-void nfs_remove_bad_delegation(struct inode *inode);
+void nfs_remove_bad_delegation(struct inode *inode, const nfs4_stateid *stateid);
 
 void nfs_delegation_mark_reclaim(struct nfs_client *clp);
 void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
 
+void nfs_mark_test_expired_all_delegations(struct nfs_client *clp);
+void nfs_reap_expired_delegations(struct nfs_client *clp);
+
 /* NFSv4 delegation-related procedures */
 int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync);
 int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid, fmode_t type);
@@ -62,6 +66,8 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
 int nfs4_have_delegation(struct inode *inode, fmode_t flags);
 int nfs4_check_delegation(struct inode *inode, fmode_t flags);
 bool nfs4_delegation_flush_on_close(const struct inode *inode);
+void nfs_inode_find_delegation_state_and_recover(struct inode *inode,
+               const nfs4_stateid *stateid);
 
 #endif
 
index 06e0bf092ba90151b91baf848990024cc4abf416..5f1af4cd1a336e8f273114f38b165d7e83ab1421 100644 (file)
@@ -435,11 +435,11 @@ int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry)
                return 0;
 
        nfsi = NFS_I(inode);
-       if (entry->fattr->fileid == nfsi->fileid)
-               return 1;
-       if (nfs_compare_fh(entry->fh, &nfsi->fh) == 0)
-               return 1;
-       return 0;
+       if (entry->fattr->fileid != nfsi->fileid)
+               return 0;
+       if (entry->fh->size && nfs_compare_fh(entry->fh, &nfsi->fh) != 0)
+               return 0;
+       return 1;
 }
 
 static
@@ -496,6 +496,14 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
                return;
        if (!(entry->fattr->valid & NFS_ATTR_FATTR_FSID))
                return;
+       if (filename.len == 0)
+               return;
+       /* Validate that the name doesn't contain any illegal '\0' */
+       if (strnlen(filename.name, filename.len) != filename.len)
+               return;
+       /* ...or '/' */
+       if (strnchr(filename.name, filename.len, '/'))
+               return;
        if (filename.name[0] == '.') {
                if (filename.len == 1)
                        return;
@@ -517,6 +525,8 @@ again:
                                        &entry->fattr->fsid))
                        goto out;
                if (nfs_same_file(dentry, entry)) {
+                       if (!entry->fh->size)
+                               goto out;
                        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
                        status = nfs_refresh_inode(d_inode(dentry), entry->fattr);
                        if (!status)
@@ -529,6 +539,10 @@ again:
                        goto again;
                }
        }
+       if (!entry->fh->size) {
+               d_lookup_done(dentry);
+               goto out;
+       }
 
        inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr, entry->label);
        alias = d_splice_alias(inode, dentry);
index 72b7d13ee3c6a14e489f6a2b94c2caf66ef8598f..bd81bcf3ffcf3cabeed98274df0f839d33088d1d 100644 (file)
@@ -387,7 +387,7 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq)
                dreq->iocb->ki_complete(dreq->iocb, res, 0);
        }
 
-       complete_all(&dreq->completion);
+       complete(&dreq->completion);
 
        nfs_direct_req_release(dreq);
 }
index 2efbdde36c3e851d9ce28bd03183c7e7f2f30cb7..9ea85ae23c320b2dcb7409c97c3da3fa93a4fc12 100644 (file)
@@ -520,7 +520,9 @@ const struct address_space_operations nfs_file_aops = {
        .invalidatepage = nfs_invalidate_page,
        .releasepage = nfs_release_page,
        .direct_IO = nfs_direct_IO,
+#ifdef CONFIG_MIGRATION
        .migratepage = nfs_migrate_page,
+#endif
        .launder_page = nfs_launder_page,
        .is_dirty_writeback = nfs_check_dirty_writeback,
        .error_remove_page = generic_error_remove_page,
@@ -685,11 +687,6 @@ out_noconflict:
        goto out;
 }
 
-static int do_vfs_lock(struct file *file, struct file_lock *fl)
-{
-       return locks_lock_file_wait(file, fl);
-}
-
 static int
 do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
 {
@@ -722,7 +719,7 @@ do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
        if (!is_local)
                status = NFS_PROTO(inode)->lock(filp, cmd, fl);
        else
-               status = do_vfs_lock(filp, fl);
+               status = locks_lock_file_wait(filp, fl);
        return status;
 }
 
@@ -747,7 +744,7 @@ do_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
        if (!is_local)
                status = NFS_PROTO(inode)->lock(filp, cmd, fl);
        else
-               status = do_vfs_lock(filp, fl);
+               status = locks_lock_file_wait(filp, fl);
        if (status < 0)
                goto out;
 
index 51b51369704c56c00732b10ff0c31edfd0d9c19e..98ace127bf861e723647019a0c8ad856f5ee0391 100644 (file)
@@ -1080,7 +1080,7 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task,
        case -NFS4ERR_BAD_STATEID:
                if (state == NULL)
                        break;
-               nfs_remove_bad_delegation(state->inode);
+               nfs_remove_bad_delegation(state->inode, NULL);
        case -NFS4ERR_OPENMODE:
                if (state == NULL)
                        break;
index a6acce6632197be17dc55eac02a6a554b66f7159..80bcc0befb07c524acedb02ae13cc215a24d94e8 100644 (file)
@@ -534,12 +534,9 @@ void nfs_clear_pnfs_ds_commit_verifiers(struct pnfs_ds_commit_info *cinfo)
 }
 #endif
 
-
 #ifdef CONFIG_MIGRATION
 extern int nfs_migrate_page(struct address_space *,
                struct page *, struct page *, enum migrate_mode);
-#else
-#define nfs_migrate_page NULL
 #endif
 
 static inline int
@@ -562,7 +559,6 @@ void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo,
 extern ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq);
 
 /* nfs4proc.c */
-extern void __nfs4_read_done_cb(struct nfs_pgio_header *);
 extern struct nfs_client *nfs4_init_client(struct nfs_client *clp,
                            const struct nfs_client_initdata *);
 extern int nfs40_walk_client_list(struct nfs_client *clp,
@@ -571,6 +567,9 @@ extern int nfs40_walk_client_list(struct nfs_client *clp,
 extern int nfs41_walk_client_list(struct nfs_client *clp,
                                struct nfs_client **result,
                                struct rpc_cred *cred);
+extern int nfs4_test_session_trunk(struct rpc_clnt *,
+                               struct rpc_xprt *,
+                               void *);
 
 static inline struct inode *nfs_igrab_and_active(struct inode *inode)
 {
index f0e06e4acbef188988547eb4d8bf66178e6d3183..fbce0d885d4c2dc77bb98f604ec881175e945eb7 100644 (file)
@@ -29,7 +29,7 @@ struct nfs_net {
        int cb_users[NFS4_MAX_MINOR_VERSION + 1];
 #endif
        spinlock_t nfs_client_lock;
-       struct timespec boot_time;
+       ktime_t boot_time;
 #ifdef CONFIG_PROC_FS
        struct proc_dir_entry *proc_nfsfs;
 #endif
index 64b43b4ad9dd8841cbfc41e86c046f89e7c4c46d..608501971fe06f2fce96044ab995071a098abc66 100644 (file)
@@ -443,6 +443,7 @@ int nfs42_proc_layoutstats_generic(struct nfs_server *server,
        task = rpc_run_task(&task_setup);
        if (IS_ERR(task))
                return PTR_ERR(task);
+       rpc_put_task(task);
        return 0;
 }
 
index 9bf64eacba5bd6d47a04ca3c9ac66b74912bdc72..9b3a82abab079f0a03047260ed2ea4e3ee9154ed 100644 (file)
@@ -39,6 +39,7 @@ enum nfs4_client_state {
        NFS4CLNT_BIND_CONN_TO_SESSION,
        NFS4CLNT_MOVED,
        NFS4CLNT_LEASE_MOVED,
+       NFS4CLNT_DELEGATION_EXPIRED,
 };
 
 #define NFS4_RENEW_TIMEOUT             0x01
@@ -57,8 +58,11 @@ struct nfs4_minor_version_ops {
                        struct nfs_fsinfo *);
        void    (*free_lock_state)(struct nfs_server *,
                        struct nfs4_lock_state *);
+       int     (*test_and_free_expired)(struct nfs_server *,
+                       nfs4_stateid *, struct rpc_cred *);
        struct nfs_seqid *
                (*alloc_seqid)(struct nfs_seqid_counter *, gfp_t);
+       int     (*session_trunk)(struct rpc_clnt *, struct rpc_xprt *, void *);
        const struct rpc_call_ops *call_sync_ops;
        const struct nfs4_state_recovery_ops *reboot_recovery_ops;
        const struct nfs4_state_recovery_ops *nograce_recovery_ops;
@@ -156,6 +160,7 @@ enum {
        NFS_STATE_RECLAIM_NOGRACE,      /* OPEN stateid needs to recover state */
        NFS_STATE_POSIX_LOCKS,          /* Posix locks are supported */
        NFS_STATE_RECOVERY_FAILED,      /* OPEN stateid state recovery failed */
+       NFS_STATE_MAY_NOTIFY_LOCK,      /* server may CB_NOTIFY_LOCK */
 };
 
 struct nfs4_state {
@@ -203,6 +208,11 @@ struct nfs4_state_recovery_ops {
                struct rpc_cred *);
 };
 
+struct nfs4_add_xprt_data {
+       struct nfs_client       *clp;
+       struct rpc_cred         *cred;
+};
+
 struct nfs4_state_maintenance_ops {
        int (*sched_state_renewal)(struct nfs_client *, struct rpc_cred *, unsigned);
        struct rpc_cred * (*get_state_renewal_cred_locked)(struct nfs_client *);
@@ -278,6 +288,8 @@ extern int nfs4_proc_get_lease_time(struct nfs_client *clp,
                struct nfs_fsinfo *fsinfo);
 extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data,
                                  bool sync);
+extern int nfs4_detect_session_trunking(struct nfs_client *clp,
+               struct nfs41_exchange_id_res *res, struct rpc_xprt *xprt);
 
 static inline bool
 is_ds_only_client(struct nfs_client *clp)
@@ -439,7 +451,7 @@ extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp);
 extern int nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *);
 extern int nfs4_schedule_migration_recovery(const struct nfs_server *);
 extern void nfs4_schedule_lease_moved_recovery(struct nfs_client *);
-extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags);
+extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags, bool);
 extern void nfs41_handle_server_scope(struct nfs_client *,
                                      struct nfs41_server_scope **);
 extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
@@ -471,6 +483,7 @@ extern struct nfs_subversion nfs_v4;
 struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *, struct nfs_subversion *);
 extern bool nfs4_disable_idmapping;
 extern unsigned short max_session_slots;
+extern unsigned short max_session_cb_slots;
 extern unsigned short send_implementation_id;
 extern bool recover_lost_locks;
 
index cd3b7cfdde16ae0e16cc17fbc9389ffc60eb511a..074ac7131459ebc378ef3a1cf50244ff758f2a88 100644 (file)
@@ -199,6 +199,9 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
        clp->cl_minorversion = cl_init->minorversion;
        clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
        clp->cl_mig_gen = 1;
+#if IS_ENABLED(CONFIG_NFS_V4_1)
+       init_waitqueue_head(&clp->cl_lock_waitq);
+#endif
        return clp;
 
 error:
@@ -562,15 +565,15 @@ out:
 /*
  * Returns true if the client IDs match
  */
-static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
+static bool nfs4_match_clientids(u64 a, u64 b)
 {
-       if (a->cl_clientid != b->cl_clientid) {
+       if (a != b) {
                dprintk("NFS: --> %s client ID %llx does not match %llx\n",
-                       __func__, a->cl_clientid, b->cl_clientid);
+                       __func__, a, b);
                return false;
        }
        dprintk("NFS: --> %s client ID %llx matches %llx\n",
-               __func__, a->cl_clientid, b->cl_clientid);
+               __func__, a, b);
        return true;
 }
 
@@ -578,17 +581,15 @@ static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
  * Returns true if the server major ids match
  */
 static bool
-nfs4_check_clientid_trunking(struct nfs_client *a, struct nfs_client *b)
+nfs4_check_serverowner_major_id(struct nfs41_server_owner *o1,
+                               struct nfs41_server_owner *o2)
 {
-       struct nfs41_server_owner *o1 = a->cl_serverowner;
-       struct nfs41_server_owner *o2 = b->cl_serverowner;
-
        if (o1->major_id_sz != o2->major_id_sz)
                goto out_major_mismatch;
        if (memcmp(o1->major_id, o2->major_id, o1->major_id_sz) != 0)
                goto out_major_mismatch;
 
-       dprintk("NFS: --> %s server owners match\n", __func__);
+       dprintk("NFS: --> %s server owner major IDs match\n", __func__);
        return true;
 
 out_major_mismatch:
@@ -597,6 +598,100 @@ out_major_mismatch:
        return false;
 }
 
+/*
+ * Returns true if server minor ids match
+ */
+static bool
+nfs4_check_serverowner_minor_id(struct nfs41_server_owner *o1,
+                               struct nfs41_server_owner *o2)
+{
+       /* Check eir_server_owner so_minor_id */
+       if (o1->minor_id != o2->minor_id)
+               goto out_minor_mismatch;
+
+       dprintk("NFS: --> %s server owner minor IDs match\n", __func__);
+       return true;
+
+out_minor_mismatch:
+       dprintk("NFS: --> %s server owner minor IDs do not match\n", __func__);
+       return false;
+}
+
+/*
+ * Returns true if the server scopes match
+ */
+static bool
+nfs4_check_server_scope(struct nfs41_server_scope *s1,
+                       struct nfs41_server_scope *s2)
+{
+       if (s1->server_scope_sz != s2->server_scope_sz)
+               goto out_scope_mismatch;
+       if (memcmp(s1->server_scope, s2->server_scope,
+                  s1->server_scope_sz) != 0)
+               goto out_scope_mismatch;
+
+       dprintk("NFS: --> %s server scopes match\n", __func__);
+       return true;
+
+out_scope_mismatch:
+       dprintk("NFS: --> %s server scopes do not match\n",
+               __func__);
+       return false;
+}
+
+/**
+ * nfs4_detect_session_trunking - Checks for session trunking.
+ *
+ * Called after a successful EXCHANGE_ID on a multi-addr connection.
+ * Upon success, add the transport.
+ *
+ * @clp:    original mount nfs_client
+ * @res:    result structure from an exchange_id using the original mount
+ *          nfs_client with a new multi_addr transport
+ *
+ * Returns zero on success, otherwise -EINVAL
+ *
+ * Note: since the exchange_id for the new multi_addr transport uses the
+ * same nfs_client from the original mount, the cl_owner_id is reused,
+ * so eir_clientowner is the same.
+ */
+int nfs4_detect_session_trunking(struct nfs_client *clp,
+                                struct nfs41_exchange_id_res *res,
+                                struct rpc_xprt *xprt)
+{
+       /* Check eir_clientid */
+       if (!nfs4_match_clientids(clp->cl_clientid, res->clientid))
+               goto out_err;
+
+       /* Check eir_server_owner so_major_id */
+       if (!nfs4_check_serverowner_major_id(clp->cl_serverowner,
+                                            res->server_owner))
+               goto out_err;
+
+       /* Check eir_server_owner so_minor_id */
+       if (!nfs4_check_serverowner_minor_id(clp->cl_serverowner,
+                                            res->server_owner))
+               goto out_err;
+
+       /* Check eir_server_scope */
+       if (!nfs4_check_server_scope(clp->cl_serverscope, res->server_scope))
+               goto out_err;
+
+       /* Session trunking passed, add the xprt */
+       rpc_clnt_xprt_switch_add_xprt(clp->cl_rpcclient, xprt);
+
+       pr_info("NFS:  %s: Session trunking succeeded for %s\n",
+               clp->cl_hostname,
+               xprt->address_strings[RPC_DISPLAY_ADDR]);
+
+       return 0;
+out_err:
+       pr_info("NFS:  %s: Session trunking failed for %s\n", clp->cl_hostname,
+               xprt->address_strings[RPC_DISPLAY_ADDR]);
+
+       return -EINVAL;
+}
+
 /**
  * nfs41_walk_client_list - Find nfs_client that matches a client/server owner
  *
@@ -650,7 +745,7 @@ int nfs41_walk_client_list(struct nfs_client *new,
                if (pos->cl_cons_state != NFS_CS_READY)
                        continue;
 
-               if (!nfs4_match_clientids(pos, new))
+               if (!nfs4_match_clientids(pos->cl_clientid, new->cl_clientid))
                        continue;
 
                /*
@@ -658,7 +753,8 @@ int nfs41_walk_client_list(struct nfs_client *new,
                 * client id trunking. In either case, we want to fall back
                 * to using the existing nfs_client.
                 */
-               if (!nfs4_check_clientid_trunking(pos, new))
+               if (!nfs4_check_serverowner_major_id(pos->cl_serverowner,
+                                                    new->cl_serverowner))
                        continue;
 
                /* Unlike NFSv4.0, we know that NFSv4.1 always uses the
index 0e327528a3ce899ae0470bd6ddc516253796879e..ad917bd72b38c3b213ce994ee2d063b111cb6277 100644 (file)
@@ -99,8 +99,8 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
 #ifdef CONFIG_NFS_V4_1
 static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
                struct rpc_cred *);
-static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *,
-               struct rpc_cred *);
+static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
+               struct rpc_cred *, bool);
 #endif
 
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
@@ -328,6 +328,33 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
        kunmap_atomic(start);
 }
 
+static void nfs4_test_and_free_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
+{
+       const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops;
+
+       ops->test_and_free_expired(server, stateid, cred);
+}
+
+static void __nfs4_free_revoked_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
+{
+       stateid->type = NFS4_REVOKED_STATEID_TYPE;
+       nfs4_test_and_free_stateid(server, stateid, cred);
+}
+
+static void nfs4_free_revoked_stateid(struct nfs_server *server,
+               const nfs4_stateid *stateid,
+               struct rpc_cred *cred)
+{
+       nfs4_stateid tmp;
+
+       nfs4_stateid_copy(&tmp, stateid);
+       __nfs4_free_revoked_stateid(server, &tmp, cred);
+}
+
 static long nfs4_update_delay(long *timeout)
 {
        long ret;
@@ -370,13 +397,23 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
        exception->delay = 0;
        exception->recovering = 0;
        exception->retry = 0;
+
+       if (stateid == NULL && state != NULL)
+               stateid = &state->stateid;
+
        switch(errorcode) {
                case 0:
                        return 0;
-               case -NFS4ERR_OPENMODE:
                case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
+               case -NFS4ERR_EXPIRED:
                case -NFS4ERR_BAD_STATEID:
+                       if (inode != NULL && stateid != NULL) {
+                               nfs_inode_find_state_and_recover(inode,
+                                               stateid);
+                               goto wait_on_recovery;
+                       }
+               case -NFS4ERR_OPENMODE:
                        if (inode) {
                                int err;
 
@@ -395,12 +432,6 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
                        if (ret < 0)
                                break;
                        goto wait_on_recovery;
-               case -NFS4ERR_EXPIRED:
-                       if (state != NULL) {
-                               ret = nfs4_schedule_stateid_recovery(server, state);
-                               if (ret < 0)
-                                       break;
-                       }
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_STALE_CLIENTID:
                        nfs4_schedule_lease_recovery(clp);
@@ -616,6 +647,7 @@ int nfs40_setup_sequence(struct nfs4_slot_table *tbl,
        }
        spin_unlock(&tbl->slot_tbl_lock);
 
+       slot->privileged = args->sa_privileged ? 1 : 0;
        args->sa_slot = slot;
        res->sr_slot = slot;
 
@@ -723,12 +755,20 @@ static int nfs41_sequence_process(struct rpc_task *task,
        /* Check the SEQUENCE operation status */
        switch (res->sr_status) {
        case 0:
+               /* If previous op on slot was interrupted and we reused
+                * the seq# and got a reply from the cache, then retry
+                */
+               if (task->tk_status == -EREMOTEIO && interrupted) {
+                       ++slot->seq_nr;
+                       goto retry_nowait;
+               }
                /* Update the slot's sequence and clientid lease timer */
                slot->seq_done = 1;
                clp = session->clp;
                do_renew_lease(clp, res->sr_timestamp);
                /* Check sequence flags */
-               nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags);
+               nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags,
+                               !!slot->privileged);
                nfs41_update_target_slotid(slot->table, slot, res);
                break;
        case 1:
@@ -875,6 +915,7 @@ int nfs41_setup_sequence(struct nfs4_session *session,
        }
        spin_unlock(&tbl->slot_tbl_lock);
 
+       slot->privileged = args->sa_privileged ? 1 : 0;
        args->sa_slot = slot;
 
        dprintk("<-- %s slotid=%u seqid=%u\n", __func__,
@@ -1353,6 +1394,19 @@ static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
        nfs4_state_set_mode_locked(state, state->state | fmode);
 }
 
+#ifdef CONFIG_NFS_V4_1
+static bool nfs_open_stateid_recover_openmode(struct nfs4_state *state)
+{
+       if (state->n_rdonly && !test_bit(NFS_O_RDONLY_STATE, &state->flags))
+               return true;
+       if (state->n_wronly && !test_bit(NFS_O_WRONLY_STATE, &state->flags))
+               return true;
+       if (state->n_rdwr && !test_bit(NFS_O_RDWR_STATE, &state->flags))
+               return true;
+       return false;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
 {
        struct nfs_client *clp = state->owner->so_server->nfs_client;
@@ -1369,11 +1423,12 @@ static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
 }
 
 static bool nfs_need_update_open_stateid(struct nfs4_state *state,
-               nfs4_stateid *stateid)
+               const nfs4_stateid *stateid, nfs4_stateid *freeme)
 {
        if (test_and_set_bit(NFS_OPEN_STATE, &state->flags) == 0)
                return true;
        if (!nfs4_stateid_match_other(stateid, &state->open_stateid)) {
+               nfs4_stateid_copy(freeme, &state->open_stateid);
                nfs_test_and_clear_all_open_stateid(state);
                return true;
        }
@@ -1437,7 +1492,9 @@ static void nfs_clear_open_stateid(struct nfs4_state *state,
                nfs4_schedule_state_manager(state->owner->so_server->nfs_client);
 }
 
-static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
+static void nfs_set_open_stateid_locked(struct nfs4_state *state,
+               const nfs4_stateid *stateid, fmode_t fmode,
+               nfs4_stateid *freeme)
 {
        switch (fmode) {
                case FMODE_READ:
@@ -1449,14 +1506,18 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *
                case FMODE_READ|FMODE_WRITE:
                        set_bit(NFS_O_RDWR_STATE, &state->flags);
        }
-       if (!nfs_need_update_open_stateid(state, stateid))
+       if (!nfs_need_update_open_stateid(state, stateid, freeme))
                return;
        if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
                nfs4_stateid_copy(&state->stateid, stateid);
        nfs4_stateid_copy(&state->open_stateid, stateid);
 }
 
-static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
+static void __update_open_stateid(struct nfs4_state *state,
+               const nfs4_stateid *open_stateid,
+               const nfs4_stateid *deleg_stateid,
+               fmode_t fmode,
+               nfs4_stateid *freeme)
 {
        /*
         * Protect the call to nfs4_state_set_mode_locked and
@@ -1469,16 +1530,22 @@ static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_s
                set_bit(NFS_DELEGATED_STATE, &state->flags);
        }
        if (open_stateid != NULL)
-               nfs_set_open_stateid_locked(state, open_stateid, fmode);
+               nfs_set_open_stateid_locked(state, open_stateid, fmode, freeme);
        write_sequnlock(&state->seqlock);
        update_open_stateflags(state, fmode);
        spin_unlock(&state->owner->so_lock);
 }
 
-static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
+static int update_open_stateid(struct nfs4_state *state,
+               const nfs4_stateid *open_stateid,
+               const nfs4_stateid *delegation,
+               fmode_t fmode)
 {
+       struct nfs_server *server = NFS_SERVER(state->inode);
+       struct nfs_client *clp = server->nfs_client;
        struct nfs_inode *nfsi = NFS_I(state->inode);
        struct nfs_delegation *deleg_cur;
+       nfs4_stateid freeme = {0};
        int ret = 0;
 
        fmode &= (FMODE_READ|FMODE_WRITE);
@@ -1500,7 +1567,8 @@ static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stat
                goto no_delegation_unlock;
 
        nfs_mark_delegation_referenced(deleg_cur);
-       __update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
+       __update_open_stateid(state, open_stateid, &deleg_cur->stateid,
+                       fmode, &freeme);
        ret = 1;
 no_delegation_unlock:
        spin_unlock(&deleg_cur->lock);
@@ -1508,11 +1576,14 @@ no_delegation:
        rcu_read_unlock();
 
        if (!ret && open_stateid != NULL) {
-               __update_open_stateid(state, open_stateid, NULL, fmode);
+               __update_open_stateid(state, open_stateid, NULL, fmode, &freeme);
                ret = 1;
        }
        if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags))
-               nfs4_schedule_state_manager(state->owner->so_server->nfs_client);
+               nfs4_schedule_state_manager(clp);
+       if (freeme.type != 0)
+               nfs4_test_and_free_stateid(server, &freeme,
+                               state->owner->so_cred);
 
        return ret;
 }
@@ -1889,7 +1960,6 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
                case -NFS4ERR_STALE_CLIENTID:
                case -NFS4ERR_STALE_STATEID:
                        set_bit(NFS_DELEGATED_STATE, &state->flags);
-               case -NFS4ERR_EXPIRED:
                        /* Don't recall a delegation if it was lost */
                        nfs4_schedule_lease_recovery(server->nfs_client);
                        return -EAGAIN;
@@ -1901,6 +1971,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
                        return -EAGAIN;
                case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
+               case -NFS4ERR_EXPIRED:
                case -NFS4ERR_BAD_STATEID:
                case -NFS4ERR_OPENMODE:
                        nfs_inode_find_state_and_recover(state->inode,
@@ -2382,9 +2453,10 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
        return ret;
 }
 
-static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state)
+static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state,
+               const nfs4_stateid *stateid)
 {
-       nfs_remove_bad_delegation(state->inode);
+       nfs_remove_bad_delegation(state->inode, stateid);
        write_seqlock(&state->seqlock);
        nfs4_stateid_copy(&state->stateid, &state->open_stateid);
        write_sequnlock(&state->seqlock);
@@ -2394,7 +2466,7 @@ static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state)
 static void nfs40_clear_delegation_stateid(struct nfs4_state *state)
 {
        if (rcu_access_pointer(NFS_I(state->inode)->delegation) != NULL)
-               nfs_finish_clear_delegation_stateid(state);
+               nfs_finish_clear_delegation_stateid(state, NULL);
 }
 
 static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
@@ -2404,7 +2476,45 @@ static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
        return nfs4_open_expired(sp, state);
 }
 
+static int nfs40_test_and_free_expired_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
+{
+       return -NFS4ERR_BAD_STATEID;
+}
+
 #if defined(CONFIG_NFS_V4_1)
+static int nfs41_test_and_free_expired_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
+{
+       int status;
+
+       switch (stateid->type) {
+       default:
+               break;
+       case NFS4_INVALID_STATEID_TYPE:
+       case NFS4_SPECIAL_STATEID_TYPE:
+               return -NFS4ERR_BAD_STATEID;
+       case NFS4_REVOKED_STATEID_TYPE:
+               goto out_free;
+       }
+
+       status = nfs41_test_stateid(server, stateid, cred);
+       switch (status) {
+       case -NFS4ERR_EXPIRED:
+       case -NFS4ERR_ADMIN_REVOKED:
+       case -NFS4ERR_DELEG_REVOKED:
+               break;
+       default:
+               return status;
+       }
+out_free:
+       /* Ack the revoked state to the server */
+       nfs41_free_stateid(server, stateid, cred, true);
+       return -NFS4ERR_EXPIRED;
+}
+
 static void nfs41_check_delegation_stateid(struct nfs4_state *state)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
@@ -2422,22 +2532,67 @@ static void nfs41_check_delegation_stateid(struct nfs4_state *state)
        }
 
        nfs4_stateid_copy(&stateid, &delegation->stateid);
+       if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
+               rcu_read_unlock();
+               nfs_finish_clear_delegation_stateid(state, &stateid);
+               return;
+       }
+
+       if (!test_and_clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags)) {
+               rcu_read_unlock();
+               return;
+       }
+
        cred = get_rpccred(delegation->cred);
        rcu_read_unlock();
-       status = nfs41_test_stateid(server, &stateid, cred);
+       status = nfs41_test_and_free_expired_stateid(server, &stateid, cred);
        trace_nfs4_test_delegation_stateid(state, NULL, status);
-
-       if (status != NFS_OK) {
-               /* Free the stateid unless the server explicitly
-                * informs us the stateid is unrecognized. */
-               if (status != -NFS4ERR_BAD_STATEID)
-                       nfs41_free_stateid(server, &stateid, cred);
-               nfs_finish_clear_delegation_stateid(state);
-       }
+       if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID)
+               nfs_finish_clear_delegation_stateid(state, &stateid);
 
        put_rpccred(cred);
 }
 
+/**
+ * nfs41_check_expired_locks - possibly free a lock stateid
+ *
+ * @state: NFSv4 state for an inode
+ *
+ * Returns NFS_OK if recovery for this stateid is now finished.
+ * Otherwise a negative NFS4ERR value is returned.
+ */
+static int nfs41_check_expired_locks(struct nfs4_state *state)
+{
+       int status, ret = NFS_OK;
+       struct nfs4_lock_state *lsp;
+       struct nfs_server *server = NFS_SERVER(state->inode);
+
+       if (!test_bit(LK_STATE_IN_USE, &state->flags))
+               goto out;
+       list_for_each_entry(lsp, &state->lock_states, ls_locks) {
+               if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
+                       struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
+
+                       status = nfs41_test_and_free_expired_stateid(server,
+                                       &lsp->ls_stateid,
+                                       cred);
+                       trace_nfs4_test_lock_stateid(state, lsp, status);
+                       if (status == -NFS4ERR_EXPIRED ||
+                           status == -NFS4ERR_BAD_STATEID) {
+                               clear_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags);
+                               lsp->ls_stateid.type = NFS4_INVALID_STATEID_TYPE;
+                               if (!recover_lost_locks)
+                                       set_bit(NFS_LOCK_LOST, &lsp->ls_flags);
+                       } else if (status != NFS_OK) {
+                               ret = status;
+                               break;
+                       }
+               }
+       };
+out:
+       return ret;
+}
+
 /**
  * nfs41_check_open_stateid - possibly free an open stateid
  *
@@ -2453,26 +2608,28 @@ static int nfs41_check_open_stateid(struct nfs4_state *state)
        struct rpc_cred *cred = state->owner->so_cred;
        int status;
 
-       /* If a state reset has been done, test_stateid is unneeded */
-       if ((test_bit(NFS_O_RDONLY_STATE, &state->flags) == 0) &&
-           (test_bit(NFS_O_WRONLY_STATE, &state->flags) == 0) &&
-           (test_bit(NFS_O_RDWR_STATE, &state->flags) == 0))
+       if (test_bit(NFS_OPEN_STATE, &state->flags) == 0) {
+               if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)  {
+                       if (nfs4_have_delegation(state->inode, state->state))
+                               return NFS_OK;
+                       return -NFS4ERR_OPENMODE;
+               }
                return -NFS4ERR_BAD_STATEID;
-
-       status = nfs41_test_stateid(server, stateid, cred);
+       }
+       status = nfs41_test_and_free_expired_stateid(server, stateid, cred);
        trace_nfs4_test_open_stateid(state, NULL, status);
-       if (status != NFS_OK) {
-               /* Free the stateid unless the server explicitly
-                * informs us the stateid is unrecognized. */
-               if (status != -NFS4ERR_BAD_STATEID)
-                       nfs41_free_stateid(server, stateid, cred);
-
+       if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) {
                clear_bit(NFS_O_RDONLY_STATE, &state->flags);
                clear_bit(NFS_O_WRONLY_STATE, &state->flags);
                clear_bit(NFS_O_RDWR_STATE, &state->flags);
                clear_bit(NFS_OPEN_STATE, &state->flags);
+               stateid->type = NFS4_INVALID_STATEID_TYPE;
        }
-       return status;
+       if (status != NFS_OK)
+               return status;
+       if (nfs_open_stateid_recover_openmode(state))
+               return -NFS4ERR_OPENMODE;
+       return NFS_OK;
 }
 
 static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
@@ -2480,6 +2637,9 @@ static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
        int status;
 
        nfs41_check_delegation_stateid(state);
+       status = nfs41_check_expired_locks(state);
+       if (status != NFS_OK)
+               return status;
        status = nfs41_check_open_stateid(state);
        if (status != NFS_OK)
                status = nfs4_open_expired(sp, state);
@@ -2537,6 +2697,8 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
                goto out;
        if (server->caps & NFS_CAP_POSIX_LOCK)
                set_bit(NFS_STATE_POSIX_LOCKS, &state->flags);
+       if (opendata->o_res.rflags & NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK)
+               set_bit(NFS_STATE_MAY_NOTIFY_LOCK, &state->flags);
 
        dentry = opendata->dentry;
        if (d_really_is_negative(dentry)) {
@@ -2899,9 +3061,12 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                        break;
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_STALE_STATEID:
+               case -NFS4ERR_EXPIRED:
+                       nfs4_free_revoked_stateid(server,
+                                       &calldata->arg.stateid,
+                                       task->tk_msg.rpc_cred);
                case -NFS4ERR_OLD_STATEID:
                case -NFS4ERR_BAD_STATEID:
-               case -NFS4ERR_EXPIRED:
                        if (!nfs4_stateid_match(&calldata->arg.stateid,
                                                &state->open_stateid)) {
                                rpc_restart_call_prepare(task);
@@ -4312,7 +4477,7 @@ static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, s
        if (error == 0) {
                /* block layout checks this! */
                server->pnfs_blksize = fsinfo->blksize;
-               set_pnfs_layoutdriver(server, fhandle, fsinfo->layouttype);
+               set_pnfs_layoutdriver(server, fhandle, fsinfo);
        }
 
        return error;
@@ -4399,24 +4564,25 @@ static bool nfs4_error_stateid_expired(int err)
        return false;
 }
 
-void __nfs4_read_done_cb(struct nfs_pgio_header *hdr)
-{
-       nfs_invalidate_atime(hdr->inode);
-}
-
 static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_pgio_header *hdr)
 {
        struct nfs_server *server = NFS_SERVER(hdr->inode);
 
        trace_nfs4_read(hdr, task->tk_status);
-       if (nfs4_async_handle_error(task, server,
-                                   hdr->args.context->state,
-                                   NULL) == -EAGAIN) {
-               rpc_restart_call_prepare(task);
-               return -EAGAIN;
+       if (task->tk_status < 0) {
+               struct nfs4_exception exception = {
+                       .inode = hdr->inode,
+                       .state = hdr->args.context->state,
+                       .stateid = &hdr->args.stateid,
+               };
+               task->tk_status = nfs4_async_handle_exception(task,
+                               server, task->tk_status, &exception);
+               if (exception.retry) {
+                       rpc_restart_call_prepare(task);
+                       return -EAGAIN;
+               }
        }
 
-       __nfs4_read_done_cb(hdr);
        if (task->tk_status > 0)
                renew_lease(server, hdr->timestamp);
        return 0;
@@ -4445,6 +4611,8 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
                return -EAGAIN;
        if (nfs4_read_stateid_changed(task, &hdr->args))
                return -EAGAIN;
+       if (task->tk_status > 0)
+               nfs_invalidate_atime(hdr->inode);
        return hdr->pgio_done_cb ? hdr->pgio_done_cb(task, hdr) :
                                    nfs4_read_done_cb(task, hdr);
 }
@@ -4482,11 +4650,19 @@ static int nfs4_write_done_cb(struct rpc_task *task,
        struct inode *inode = hdr->inode;
 
        trace_nfs4_write(hdr, task->tk_status);
-       if (nfs4_async_handle_error(task, NFS_SERVER(inode),
-                                   hdr->args.context->state,
-                                   NULL) == -EAGAIN) {
-               rpc_restart_call_prepare(task);
-               return -EAGAIN;
+       if (task->tk_status < 0) {
+               struct nfs4_exception exception = {
+                       .inode = hdr->inode,
+                       .state = hdr->args.context->state,
+                       .stateid = &hdr->args.stateid,
+               };
+               task->tk_status = nfs4_async_handle_exception(task,
+                               NFS_SERVER(inode), task->tk_status,
+                               &exception);
+               if (exception.retry) {
+                       rpc_restart_call_prepare(task);
+                       return -EAGAIN;
+               }
        }
        if (task->tk_status >= 0) {
                renew_lease(NFS_SERVER(inode), hdr->timestamp);
@@ -5123,12 +5299,14 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
        if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
                /* An impossible timestamp guarantees this value
                 * will never match a generated boot time. */
-               verf[0] = 0;
-               verf[1] = cpu_to_be32(NSEC_PER_SEC + 1);
+               verf[0] = cpu_to_be32(U32_MAX);
+               verf[1] = cpu_to_be32(U32_MAX);
        } else {
                struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
-               verf[0] = cpu_to_be32(nn->boot_time.tv_sec);
-               verf[1] = cpu_to_be32(nn->boot_time.tv_nsec);
+               u64 ns = ktime_to_ns(nn->boot_time);
+
+               verf[0] = cpu_to_be32(ns >> 32);
+               verf[1] = cpu_to_be32(ns);
        }
        memcpy(bootverf->data, verf, sizeof(bootverf->data));
 }
@@ -5393,10 +5571,13 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
                renew_lease(data->res.server, data->timestamp);
        case -NFS4ERR_ADMIN_REVOKED:
        case -NFS4ERR_DELEG_REVOKED:
+       case -NFS4ERR_EXPIRED:
+               nfs4_free_revoked_stateid(data->res.server,
+                               data->args.stateid,
+                               task->tk_msg.rpc_cred);
        case -NFS4ERR_BAD_STATEID:
        case -NFS4ERR_OLD_STATEID:
        case -NFS4ERR_STALE_STATEID:
-       case -NFS4ERR_EXPIRED:
                task->tk_status = 0;
                if (data->roc)
                        pnfs_roc_set_barrier(data->inode, data->roc_barrier);
@@ -5528,22 +5709,6 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4
        return err;
 }
 
-#define NFS4_LOCK_MINTIMEOUT (1 * HZ)
-#define NFS4_LOCK_MAXTIMEOUT (30 * HZ)
-
-/* 
- * sleep, with exponential backoff, and retry the LOCK operation. 
- */
-static unsigned long
-nfs4_set_lock_task_retry(unsigned long timeout)
-{
-       freezable_schedule_timeout_killable_unsafe(timeout);
-       timeout <<= 1;
-       if (timeout > NFS4_LOCK_MAXTIMEOUT)
-               return NFS4_LOCK_MAXTIMEOUT;
-       return timeout;
-}
-
 static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
 {
        struct inode *inode = state->inode;
@@ -5600,11 +5765,6 @@ static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *
        return err;
 }
 
-static int do_vfs_lock(struct inode *inode, struct file_lock *fl)
-{
-       return locks_lock_inode_wait(inode, fl);
-}
-
 struct nfs4_unlockdata {
        struct nfs_locku_args arg;
        struct nfs_locku_res res;
@@ -5657,14 +5817,18 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
        switch (task->tk_status) {
                case 0:
                        renew_lease(calldata->server, calldata->timestamp);
-                       do_vfs_lock(calldata->lsp->ls_state->inode, &calldata->fl);
+                       locks_lock_inode_wait(calldata->lsp->ls_state->inode, &calldata->fl);
                        if (nfs4_update_lock_stateid(calldata->lsp,
                                        &calldata->res.stateid))
                                break;
+               case -NFS4ERR_ADMIN_REVOKED:
+               case -NFS4ERR_EXPIRED:
+                       nfs4_free_revoked_stateid(calldata->server,
+                                       &calldata->arg.stateid,
+                                       task->tk_msg.rpc_cred);
                case -NFS4ERR_BAD_STATEID:
                case -NFS4ERR_OLD_STATEID:
                case -NFS4ERR_STALE_STATEID:
-               case -NFS4ERR_EXPIRED:
                        if (!nfs4_stateid_match(&calldata->arg.stateid,
                                                &calldata->lsp->ls_stateid))
                                rpc_restart_call_prepare(task);
@@ -5765,7 +5929,7 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
        mutex_lock(&sp->so_delegreturn_mutex);
        /* Exclude nfs4_reclaim_open_stateid() - note nesting! */
        down_read(&nfsi->rwsem);
-       if (do_vfs_lock(inode, request) == -ENOENT) {
+       if (locks_lock_inode_wait(inode, request) == -ENOENT) {
                up_read(&nfsi->rwsem);
                mutex_unlock(&sp->so_delegreturn_mutex);
                goto out;
@@ -5906,7 +6070,7 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
                                data->timestamp);
                if (data->arg.new_lock) {
                        data->fl.fl_flags &= ~(FL_SLEEP | FL_ACCESS);
-                       if (do_vfs_lock(lsp->ls_state->inode, &data->fl) < 0) {
+                       if (locks_lock_inode_wait(lsp->ls_state->inode, &data->fl) < 0) {
                                rpc_restart_call_prepare(task);
                                break;
                        }
@@ -5965,6 +6129,7 @@ static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_
 {
        switch (error) {
        case -NFS4ERR_ADMIN_REVOKED:
+       case -NFS4ERR_EXPIRED:
        case -NFS4ERR_BAD_STATEID:
                lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
                if (new_lock_owner != 0 ||
@@ -5973,7 +6138,6 @@ static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_
                break;
        case -NFS4ERR_STALE_STATEID:
                lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
-       case -NFS4ERR_EXPIRED:
                nfs4_schedule_lease_recovery(server->nfs_client);
        };
 }
@@ -6083,52 +6247,19 @@ out:
 }
 
 #if defined(CONFIG_NFS_V4_1)
-/**
- * nfs41_check_expired_locks - possibly free a lock stateid
- *
- * @state: NFSv4 state for an inode
- *
- * Returns NFS_OK if recovery for this stateid is now finished.
- * Otherwise a negative NFS4ERR value is returned.
- */
-static int nfs41_check_expired_locks(struct nfs4_state *state)
-{
-       int status, ret = -NFS4ERR_BAD_STATEID;
-       struct nfs4_lock_state *lsp;
-       struct nfs_server *server = NFS_SERVER(state->inode);
-
-       list_for_each_entry(lsp, &state->lock_states, ls_locks) {
-               if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
-                       struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
-
-                       status = nfs41_test_stateid(server,
-                                       &lsp->ls_stateid,
-                                       cred);
-                       trace_nfs4_test_lock_stateid(state, lsp, status);
-                       if (status != NFS_OK) {
-                               /* Free the stateid unless the server
-                                * informs us the stateid is unrecognized. */
-                               if (status != -NFS4ERR_BAD_STATEID)
-                                       nfs41_free_stateid(server,
-                                                       &lsp->ls_stateid,
-                                                       cred);
-                               clear_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags);
-                               ret = status;
-                       }
-               }
-       };
-
-       return ret;
-}
-
 static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *request)
 {
-       int status = NFS_OK;
+       struct nfs4_lock_state *lsp;
+       int status;
 
-       if (test_bit(LK_STATE_IN_USE, &state->flags))
-               status = nfs41_check_expired_locks(state);
-       if (status != NFS_OK)
-               status = nfs4_lock_expired(state, request);
+       status = nfs4_set_lock_state(state, request);
+       if (status != 0)
+               return status;
+       lsp = request->fl_u.nfs4_fl.owner;
+       if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) ||
+           test_bit(NFS_LOCK_LOST, &lsp->ls_flags))
+               return 0;
+       status = nfs4_lock_expired(state, request);
        return status;
 }
 #endif
@@ -6138,17 +6269,10 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
        struct nfs_inode *nfsi = NFS_I(state->inode);
        struct nfs4_state_owner *sp = state->owner;
        unsigned char fl_flags = request->fl_flags;
-       int status = -ENOLCK;
+       int status;
 
-       if ((fl_flags & FL_POSIX) &&
-                       !test_bit(NFS_STATE_POSIX_LOCKS, &state->flags))
-               goto out;
-       /* Is this a delegated open? */
-       status = nfs4_set_lock_state(state, request);
-       if (status != 0)
-               goto out;
        request->fl_flags |= FL_ACCESS;
-       status = do_vfs_lock(state->inode, request);
+       status = locks_lock_inode_wait(state->inode, request);
        if (status < 0)
                goto out;
        mutex_lock(&sp->so_delegreturn_mutex);
@@ -6157,7 +6281,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
                /* Yes: cache locks! */
                /* ...but avoid races with delegation recall... */
                request->fl_flags = fl_flags & ~FL_SLEEP;
-               status = do_vfs_lock(state->inode, request);
+               status = locks_lock_inode_wait(state->inode, request);
                up_read(&nfsi->rwsem);
                mutex_unlock(&sp->so_delegreturn_mutex);
                goto out;
@@ -6188,12 +6312,124 @@ static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *
        return err;
 }
 
+#define NFS4_LOCK_MINTIMEOUT (1 * HZ)
+#define NFS4_LOCK_MAXTIMEOUT (30 * HZ)
+
+static int
+nfs4_retry_setlk_simple(struct nfs4_state *state, int cmd,
+                       struct file_lock *request)
+{
+       int             status = -ERESTARTSYS;
+       unsigned long   timeout = NFS4_LOCK_MINTIMEOUT;
+
+       while(!signalled()) {
+               status = nfs4_proc_setlk(state, cmd, request);
+               if ((status != -EAGAIN) || IS_SETLK(cmd))
+                       break;
+               freezable_schedule_timeout_interruptible(timeout);
+               timeout *= 2;
+               timeout = min_t(unsigned long, NFS4_LOCK_MAXTIMEOUT, timeout);
+               status = -ERESTARTSYS;
+       }
+       return status;
+}
+
+#ifdef CONFIG_NFS_V4_1
+struct nfs4_lock_waiter {
+       struct task_struct      *task;
+       struct inode            *inode;
+       struct nfs_lowner       *owner;
+       bool                    notified;
+};
+
+static int
+nfs4_wake_lock_waiter(wait_queue_t *wait, unsigned int mode, int flags, void *key)
+{
+       int ret;
+       struct cb_notify_lock_args *cbnl = key;
+       struct nfs4_lock_waiter *waiter = wait->private;
+       struct nfs_lowner       *lowner = &cbnl->cbnl_owner,
+                               *wowner = waiter->owner;
+
+       /* Only wake if the callback was for the same owner */
+       if (lowner->clientid != wowner->clientid ||
+           lowner->id != wowner->id             ||
+           lowner->s_dev != wowner->s_dev)
+               return 0;
+
+       /* Make sure it's for the right inode */
+       if (nfs_compare_fh(NFS_FH(waiter->inode), &cbnl->cbnl_fh))
+               return 0;
+
+       waiter->notified = true;
+
+       /* override "private" so we can use default_wake_function */
+       wait->private = waiter->task;
+       ret = autoremove_wake_function(wait, mode, flags, key);
+       wait->private = waiter;
+       return ret;
+}
+
+static int
+nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
+{
+       int status = -ERESTARTSYS;
+       unsigned long flags;
+       struct nfs4_lock_state *lsp = request->fl_u.nfs4_fl.owner;
+       struct nfs_server *server = NFS_SERVER(state->inode);
+       struct nfs_client *clp = server->nfs_client;
+       wait_queue_head_t *q = &clp->cl_lock_waitq;
+       struct nfs_lowner owner = { .clientid = clp->cl_clientid,
+                                   .id = lsp->ls_seqid.owner_id,
+                                   .s_dev = server->s_dev };
+       struct nfs4_lock_waiter waiter = { .task  = current,
+                                          .inode = state->inode,
+                                          .owner = &owner,
+                                          .notified = false };
+       wait_queue_t wait;
+
+       /* Don't bother with waitqueue if we don't expect a callback */
+       if (!test_bit(NFS_STATE_MAY_NOTIFY_LOCK, &state->flags))
+               return nfs4_retry_setlk_simple(state, cmd, request);
+
+       init_wait(&wait);
+       wait.private = &waiter;
+       wait.func = nfs4_wake_lock_waiter;
+       add_wait_queue(q, &wait);
+
+       while(!signalled()) {
+               status = nfs4_proc_setlk(state, cmd, request);
+               if ((status != -EAGAIN) || IS_SETLK(cmd))
+                       break;
+
+               status = -ERESTARTSYS;
+               spin_lock_irqsave(&q->lock, flags);
+               if (waiter.notified) {
+                       spin_unlock_irqrestore(&q->lock, flags);
+                       continue;
+               }
+               set_current_state(TASK_INTERRUPTIBLE);
+               spin_unlock_irqrestore(&q->lock, flags);
+
+               freezable_schedule_timeout_interruptible(NFS4_LOCK_MAXTIMEOUT);
+       }
+
+       finish_wait(q, &wait);
+       return status;
+}
+#else /* !CONFIG_NFS_V4_1 */
+static inline int
+nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
+{
+       return nfs4_retry_setlk_simple(state, cmd, request);
+}
+#endif
+
 static int
 nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
 {
        struct nfs_open_context *ctx;
        struct nfs4_state *state;
-       unsigned long timeout = NFS4_LOCK_MINTIMEOUT;
        int status;
 
        /* verify open state */
@@ -6220,6 +6456,11 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
 
        if (state == NULL)
                return -ENOLCK;
+
+       if ((request->fl_flags & FL_POSIX) &&
+           !test_bit(NFS_STATE_POSIX_LOCKS, &state->flags))
+               return -ENOLCK;
+
        /*
         * Don't rely on the VFS having checked the file open mode,
         * since it won't do this for flock() locks.
@@ -6234,16 +6475,11 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
                        return -EBADF;
        }
 
-       do {
-               status = nfs4_proc_setlk(state, cmd, request);
-               if ((status != -EAGAIN) || IS_SETLK(cmd))
-                       break;
-               timeout = nfs4_set_lock_task_retry(timeout);
-               status = -ERESTARTSYS;
-               if (signalled())
-                       break;
-       } while(status < 0);
-       return status;
+       status = nfs4_set_lock_state(state, request);
+       if (status != 0)
+               return status;
+
+       return nfs4_retry_setlk(state, cmd, request);
 }
 
 int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid)
@@ -7104,75 +7340,161 @@ static int nfs4_sp4_select_mode(struct nfs_client *clp,
        return 0;
 }
 
+struct nfs41_exchange_id_data {
+       struct nfs41_exchange_id_res res;
+       struct nfs41_exchange_id_args args;
+       struct rpc_xprt *xprt;
+       int rpc_status;
+};
+
+static void nfs4_exchange_id_done(struct rpc_task *task, void *data)
+{
+       struct nfs41_exchange_id_data *cdata =
+                                       (struct nfs41_exchange_id_data *)data;
+       struct nfs_client *clp = cdata->args.client;
+       int status = task->tk_status;
+
+       trace_nfs4_exchange_id(clp, status);
+
+       if (status == 0)
+               status = nfs4_check_cl_exchange_flags(cdata->res.flags);
+
+       if (cdata->xprt && status == 0) {
+               status = nfs4_detect_session_trunking(clp, &cdata->res,
+                                                     cdata->xprt);
+               goto out;
+       }
+
+       if (status  == 0)
+               status = nfs4_sp4_select_mode(clp, &cdata->res.state_protect);
+
+       if (status == 0) {
+               clp->cl_clientid = cdata->res.clientid;
+               clp->cl_exchange_flags = cdata->res.flags;
+               /* Client ID is not confirmed */
+               if (!(cdata->res.flags & EXCHGID4_FLAG_CONFIRMED_R)) {
+                       clear_bit(NFS4_SESSION_ESTABLISHED,
+                       &clp->cl_session->session_state);
+                       clp->cl_seqid = cdata->res.seqid;
+               }
+
+               kfree(clp->cl_serverowner);
+               clp->cl_serverowner = cdata->res.server_owner;
+               cdata->res.server_owner = NULL;
+
+               /* use the most recent implementation id */
+               kfree(clp->cl_implid);
+               clp->cl_implid = cdata->res.impl_id;
+               cdata->res.impl_id = NULL;
+
+               if (clp->cl_serverscope != NULL &&
+                   !nfs41_same_server_scope(clp->cl_serverscope,
+                                       cdata->res.server_scope)) {
+                       dprintk("%s: server_scope mismatch detected\n",
+                               __func__);
+                       set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
+                       kfree(clp->cl_serverscope);
+                       clp->cl_serverscope = NULL;
+               }
+
+               if (clp->cl_serverscope == NULL) {
+                       clp->cl_serverscope = cdata->res.server_scope;
+                       cdata->res.server_scope = NULL;
+               }
+               /* Save the EXCHANGE_ID verifier session trunk tests */
+               memcpy(clp->cl_confirm.data, cdata->args.verifier->data,
+                      sizeof(clp->cl_confirm.data));
+       }
+out:
+       cdata->rpc_status = status;
+       return;
+}
+
+static void nfs4_exchange_id_release(void *data)
+{
+       struct nfs41_exchange_id_data *cdata =
+                                       (struct nfs41_exchange_id_data *)data;
+
+       nfs_put_client(cdata->args.client);
+       if (cdata->xprt) {
+               xprt_put(cdata->xprt);
+               rpc_clnt_xprt_switch_put(cdata->args.client->cl_rpcclient);
+       }
+       kfree(cdata->res.impl_id);
+       kfree(cdata->res.server_scope);
+       kfree(cdata->res.server_owner);
+       kfree(cdata);
+}
+
+static const struct rpc_call_ops nfs4_exchange_id_call_ops = {
+       .rpc_call_done = nfs4_exchange_id_done,
+       .rpc_release = nfs4_exchange_id_release,
+};
+
 /*
  * _nfs4_proc_exchange_id()
  *
  * Wrapper for EXCHANGE_ID operation.
  */
 static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
-       u32 sp4_how)
+                       u32 sp4_how, struct rpc_xprt *xprt)
 {
        nfs4_verifier verifier;
-       struct nfs41_exchange_id_args args = {
-               .verifier = &verifier,
-               .client = clp,
-#ifdef CONFIG_NFS_V4_1_MIGRATION
-               .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
-                        EXCHGID4_FLAG_BIND_PRINC_STATEID |
-                        EXCHGID4_FLAG_SUPP_MOVED_MIGR,
-#else
-               .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
-                        EXCHGID4_FLAG_BIND_PRINC_STATEID,
-#endif
-       };
-       struct nfs41_exchange_id_res res = {
-               0
-       };
-       int status;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID],
-               .rpc_argp = &args,
-               .rpc_resp = &res,
                .rpc_cred = cred,
        };
+       struct rpc_task_setup task_setup_data = {
+               .rpc_client = clp->cl_rpcclient,
+               .callback_ops = &nfs4_exchange_id_call_ops,
+               .rpc_message = &msg,
+               .flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT,
+       };
+       struct nfs41_exchange_id_data *calldata;
+       struct rpc_task *task;
+       int status = -EIO;
+
+       if (!atomic_inc_not_zero(&clp->cl_count))
+               goto out;
+
+       status = -ENOMEM;
+       calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
+       if (!calldata)
+               goto out;
 
-       nfs4_init_boot_verifier(clp, &verifier);
+       if (!xprt)
+               nfs4_init_boot_verifier(clp, &verifier);
 
        status = nfs4_init_uniform_client_string(clp);
        if (status)
-               goto out;
+               goto out_calldata;
 
        dprintk("NFS call  exchange_id auth=%s, '%s'\n",
                clp->cl_rpcclient->cl_auth->au_ops->au_name,
                clp->cl_owner_id);
 
-       res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
-                                       GFP_NOFS);
-       if (unlikely(res.server_owner == NULL)) {
-               status = -ENOMEM;
-               goto out;
-       }
+       calldata->res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
+                                               GFP_NOFS);
+       status = -ENOMEM;
+       if (unlikely(calldata->res.server_owner == NULL))
+               goto out_calldata;
 
-       res.server_scope = kzalloc(sizeof(struct nfs41_server_scope),
+       calldata->res.server_scope = kzalloc(sizeof(struct nfs41_server_scope),
                                        GFP_NOFS);
-       if (unlikely(res.server_scope == NULL)) {
-               status = -ENOMEM;
+       if (unlikely(calldata->res.server_scope == NULL))
                goto out_server_owner;
-       }
 
-       res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_NOFS);
-       if (unlikely(res.impl_id == NULL)) {
-               status = -ENOMEM;
+       calldata->res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_NOFS);
+       if (unlikely(calldata->res.impl_id == NULL))
                goto out_server_scope;
-       }
 
        switch (sp4_how) {
        case SP4_NONE:
-               args.state_protect.how = SP4_NONE;
+               calldata->args.state_protect.how = SP4_NONE;
                break;
 
        case SP4_MACH_CRED:
-               args.state_protect = nfs4_sp4_mach_cred_request;
+               calldata->args.state_protect = nfs4_sp4_mach_cred_request;
                break;
 
        default:
@@ -7181,56 +7503,42 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
                status = -EINVAL;
                goto out_impl_id;
        }
+       if (xprt) {
+               calldata->xprt = xprt;
+               task_setup_data.rpc_xprt = xprt;
+               task_setup_data.flags =
+                               RPC_TASK_SOFT|RPC_TASK_SOFTCONN|RPC_TASK_ASYNC;
+               calldata->args.verifier = &clp->cl_confirm;
+       } else {
+               calldata->args.verifier = &verifier;
+       }
+       calldata->args.client = clp;
+#ifdef CONFIG_NFS_V4_1_MIGRATION
+       calldata->args.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
+       EXCHGID4_FLAG_BIND_PRINC_STATEID |
+       EXCHGID4_FLAG_SUPP_MOVED_MIGR,
+#else
+       calldata->args.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
+       EXCHGID4_FLAG_BIND_PRINC_STATEID,
+#endif
+       msg.rpc_argp = &calldata->args;
+       msg.rpc_resp = &calldata->res;
+       task_setup_data.callback_data = calldata;
 
-       status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
-       trace_nfs4_exchange_id(clp, status);
-       if (status == 0)
-               status = nfs4_check_cl_exchange_flags(res.flags);
-
-       if (status == 0)
-               status = nfs4_sp4_select_mode(clp, &res.state_protect);
-
-       if (status == 0) {
-               clp->cl_clientid = res.clientid;
-               clp->cl_exchange_flags = res.flags;
-               /* Client ID is not confirmed */
-               if (!(res.flags & EXCHGID4_FLAG_CONFIRMED_R)) {
-                       clear_bit(NFS4_SESSION_ESTABLISHED,
-                                       &clp->cl_session->session_state);
-                       clp->cl_seqid = res.seqid;
-               }
-
-               kfree(clp->cl_serverowner);
-               clp->cl_serverowner = res.server_owner;
-               res.server_owner = NULL;
-
-               /* use the most recent implementation id */
-               kfree(clp->cl_implid);
-               clp->cl_implid = res.impl_id;
-               res.impl_id = NULL;
-
-               if (clp->cl_serverscope != NULL &&
-                   !nfs41_same_server_scope(clp->cl_serverscope,
-                                            res.server_scope)) {
-                       dprintk("%s: server_scope mismatch detected\n",
-                               __func__);
-                       set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
-                       kfree(clp->cl_serverscope);
-                       clp->cl_serverscope = NULL;
-               }
-
-               if (clp->cl_serverscope == NULL) {
-                       clp->cl_serverscope = res.server_scope;
-                       res.server_scope = NULL;
-               }
+       task = rpc_run_task(&task_setup_data);
+       if (IS_ERR(task)) {
+       status = PTR_ERR(task);
+               goto out_impl_id;
        }
 
-out_impl_id:
-       kfree(res.impl_id);
-out_server_scope:
-       kfree(res.server_scope);
-out_server_owner:
-       kfree(res.server_owner);
+       if (!xprt) {
+               status = rpc_wait_for_completion_task(task);
+               if (!status)
+                       status = calldata->rpc_status;
+       } else  /* session trunking test */
+               status = calldata->rpc_status;
+
+       rpc_put_task(task);
 out:
        if (clp->cl_implid != NULL)
                dprintk("NFS reply exchange_id: Server Implementation ID: "
@@ -7240,6 +7548,16 @@ out:
                        clp->cl_implid->date.nseconds);
        dprintk("NFS reply exchange_id: %d\n", status);
        return status;
+
+out_impl_id:
+       kfree(calldata->res.impl_id);
+out_server_scope:
+       kfree(calldata->res.server_scope);
+out_server_owner:
+       kfree(calldata->res.server_owner);
+out_calldata:
+       kfree(calldata);
+       goto out;
 }
 
 /*
@@ -7262,14 +7580,45 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
        /* try SP4_MACH_CRED if krb5i/p */
        if (authflavor == RPC_AUTH_GSS_KRB5I ||
            authflavor == RPC_AUTH_GSS_KRB5P) {
-               status = _nfs4_proc_exchange_id(clp, cred, SP4_MACH_CRED);
+               status = _nfs4_proc_exchange_id(clp, cred, SP4_MACH_CRED, NULL);
                if (!status)
                        return 0;
        }
 
        /* try SP4_NONE */
-       return _nfs4_proc_exchange_id(clp, cred, SP4_NONE);
+       return _nfs4_proc_exchange_id(clp, cred, SP4_NONE, NULL);
+}
+
+/**
+ * nfs4_test_session_trunk
+ *
+ * This is an add_xprt_test() test function called from
+ * rpc_clnt_setup_test_and_add_xprt.
+ *
+ * The rpc_xprt_switch is referrenced by rpc_clnt_setup_test_and_add_xprt
+ * and is dereferrenced in nfs4_exchange_id_release
+ *
+ * Upon success, add the new transport to the rpc_clnt
+ *
+ * @clnt: struct rpc_clnt to get new transport
+ * @xprt: the rpc_xprt to test
+ * @data: call data for _nfs4_proc_exchange_id.
+ */
+int nfs4_test_session_trunk(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
+                           void *data)
+{
+       struct nfs4_add_xprt_data *adata = (struct nfs4_add_xprt_data *)data;
+       u32 sp4_how;
+
+       dprintk("--> %s try %s\n", __func__,
+               xprt->address_strings[RPC_DISPLAY_ADDR]);
+
+       sp4_how = (adata->clp->cl_sp4_flags == 0 ? SP4_NONE : SP4_MACH_CRED);
+
+       /* Test connection for session trunking. Async exchange_id call */
+       return  _nfs4_proc_exchange_id(adata->clp, adata->cred, sp4_how, xprt);
 }
+EXPORT_SYMBOL_GPL(nfs4_test_session_trunk);
 
 static int _nfs4_proc_destroy_clientid(struct nfs_client *clp,
                struct rpc_cred *cred)
@@ -7463,7 +7812,7 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args,
        args->bc_attrs.max_resp_sz = max_bc_payload;
        args->bc_attrs.max_resp_sz_cached = 0;
        args->bc_attrs.max_ops = NFS4_MAX_BACK_CHANNEL_OPS;
-       args->bc_attrs.max_reqs = NFS41_BC_MAX_CALLBACKS;
+       args->bc_attrs.max_reqs = min_t(unsigned short, max_session_cb_slots, 1);
 
        dprintk("%s: Back Channel : max_rqst_sz=%u max_resp_sz=%u "
                "max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n",
@@ -7510,10 +7859,9 @@ static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args
                return -EINVAL;
        if (rcvd->max_resp_sz_cached > sent->max_resp_sz_cached)
                return -EINVAL;
-       /* These would render the backchannel useless: */
-       if (rcvd->max_ops != sent->max_ops)
+       if (rcvd->max_ops > sent->max_ops)
                return -EINVAL;
-       if (rcvd->max_reqs != sent->max_reqs)
+       if (rcvd->max_reqs > sent->max_reqs)
                return -EINVAL;
 out:
        return 0;
@@ -7982,6 +8330,8 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
        case -NFS4ERR_RECALLCONFLICT:
                status = -ERECALLCONFLICT;
                break;
+       case -NFS4ERR_DELEG_REVOKED:
+       case -NFS4ERR_ADMIN_REVOKED:
        case -NFS4ERR_EXPIRED:
        case -NFS4ERR_BAD_STATEID:
                exception->timeout = 0;
@@ -7993,6 +8343,7 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
                                        &lgp->args.ctx->state->stateid)) {
                        spin_unlock(&inode->i_lock);
                        exception->state = lgp->args.ctx->state;
+                       exception->stateid = &lgp->args.stateid;
                        break;
                }
 
@@ -8591,6 +8942,24 @@ static int _nfs41_test_stateid(struct nfs_server *server,
        return -res.status;
 }
 
+static void nfs4_handle_delay_or_session_error(struct nfs_server *server,
+               int err, struct nfs4_exception *exception)
+{
+       exception->retry = 0;
+       switch(err) {
+       case -NFS4ERR_DELAY:
+       case -NFS4ERR_RETRY_UNCACHED_REP:
+               nfs4_handle_exception(server, err, exception);
+               break;
+       case -NFS4ERR_BADSESSION:
+       case -NFS4ERR_BADSLOT:
+       case -NFS4ERR_BAD_HIGH_SLOT:
+       case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+       case -NFS4ERR_DEADSESSION:
+               nfs4_do_handle_exception(server, err, exception);
+       }
+}
+
 /**
  * nfs41_test_stateid - perform a TEST_STATEID operation
  *
@@ -8610,9 +8979,7 @@ static int nfs41_test_stateid(struct nfs_server *server,
        int err;
        do {
                err = _nfs41_test_stateid(server, stateid, cred);
-               if (err != -NFS4ERR_DELAY)
-                       break;
-               nfs4_handle_exception(server, err, &exception);
+               nfs4_handle_delay_or_session_error(server, err, &exception);
        } while (exception.retry);
        return err;
 }
@@ -8657,7 +9024,7 @@ static const struct rpc_call_ops nfs41_free_stateid_ops = {
 };
 
 static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
-               nfs4_stateid *stateid,
+               const nfs4_stateid *stateid,
                struct rpc_cred *cred,
                bool privileged)
 {
@@ -8687,7 +9054,7 @@ static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
 
        msg.rpc_argp = &data->args;
        msg.rpc_resp = &data->res;
-       nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0);
+       nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
        if (privileged)
                nfs4_set_sequence_privileged(&data->args.seq_args);
 
@@ -8700,38 +9067,31 @@ static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
  * @server: server / transport on which to perform the operation
  * @stateid: state ID to release
  * @cred: credential
+ * @is_recovery: set to true if this call needs to be privileged
  *
- * Returns NFS_OK if the server freed "stateid".  Otherwise a
- * negative NFS4ERR value is returned.
+ * Note: this function is always asynchronous.
  */
 static int nfs41_free_stateid(struct nfs_server *server,
-               nfs4_stateid *stateid,
-               struct rpc_cred *cred)
+               const nfs4_stateid *stateid,
+               struct rpc_cred *cred,
+               bool is_recovery)
 {
        struct rpc_task *task;
-       int ret;
 
-       task = _nfs41_free_stateid(server, stateid, cred, true);
+       task = _nfs41_free_stateid(server, stateid, cred, is_recovery);
        if (IS_ERR(task))
                return PTR_ERR(task);
-       ret = rpc_wait_for_completion_task(task);
-       if (!ret)
-               ret = task->tk_status;
        rpc_put_task(task);
-       return ret;
+       return 0;
 }
 
 static void
 nfs41_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
 {
-       struct rpc_task *task;
        struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
 
-       task = _nfs41_free_stateid(server, &lsp->ls_stateid, cred, false);
+       nfs41_free_stateid(server, &lsp->ls_stateid, cred, false);
        nfs4_free_lock_state(server, lsp);
-       if (IS_ERR(task))
-               return;
-       rpc_put_task(task);
 }
 
 static bool nfs41_match_stateid(const nfs4_stateid *s1,
@@ -8835,6 +9195,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
        .match_stateid = nfs4_match_stateid,
        .find_root_sec = nfs4_find_root_sec,
        .free_lock_state = nfs4_release_lockowner,
+       .test_and_free_expired = nfs40_test_and_free_expired_stateid,
        .alloc_seqid = nfs_alloc_seqid,
        .call_sync_ops = &nfs40_call_sync_ops,
        .reboot_recovery_ops = &nfs40_reboot_recovery_ops,
@@ -8862,7 +9223,9 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
        .match_stateid = nfs41_match_stateid,
        .find_root_sec = nfs41_find_root_sec,
        .free_lock_state = nfs41_free_lock_state,
+       .test_and_free_expired = nfs41_test_and_free_expired_stateid,
        .alloc_seqid = nfs_alloc_no_seqid,
+       .session_trunk = nfs4_test_session_trunk,
        .call_sync_ops = &nfs41_call_sync_ops,
        .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
@@ -8891,7 +9254,9 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
        .find_root_sec = nfs41_find_root_sec,
        .free_lock_state = nfs41_free_lock_state,
        .call_sync_ops = &nfs41_call_sync_ops,
+       .test_and_free_expired = nfs41_test_and_free_expired_stateid,
        .alloc_seqid = nfs_alloc_no_seqid,
+       .session_trunk = nfs4_test_session_trunk,
        .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
        .state_renewal_ops = &nfs41_state_renewal_ops,
index f703b755351bd8ad2217fb18009e93232f2ccc61..dae3855000050f758380cc779d1c775abd305478 100644 (file)
@@ -9,6 +9,7 @@
 
 /* maximum number of slots to use */
 #define NFS4_DEF_SLOT_TABLE_SIZE (64U)
+#define NFS4_DEF_CB_SLOT_TABLE_SIZE (1U)
 #define NFS4_MAX_SLOT_TABLE (1024U)
 #define NFS4_NO_SLOT ((u32)-1)
 
@@ -22,6 +23,7 @@ struct nfs4_slot {
        u32                     slot_nr;
        u32                     seq_nr;
        unsigned int            interrupted : 1,
+                               privileged : 1,
                                seq_done : 1;
 };
 
index cada00aa5096d7dbd57a0b8717d1a6583abbbfa7..5f4281ec5f72c3556d76a479854d6fb378d03124 100644 (file)
@@ -991,6 +991,8 @@ int nfs4_select_rw_stateid(struct nfs4_state *state,
 {
        int ret;
 
+       if (!nfs4_valid_open_stateid(state))
+               return -EIO;
        if (cred != NULL)
                *cred = NULL;
        ret = nfs4_copy_lock_stateid(dst, state, lockowner);
@@ -1303,6 +1305,8 @@ void nfs4_schedule_path_down_recovery(struct nfs_client *clp)
 static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
 {
 
+       if (!nfs4_valid_open_stateid(state))
+               return 0;
        set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
        /* Don't recover state that expired before the reboot */
        if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) {
@@ -1316,6 +1320,8 @@ static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_st
 
 int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
 {
+       if (!nfs4_valid_open_stateid(state))
+               return 0;
        set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
        clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
        set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags);
@@ -1327,9 +1333,8 @@ int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_
 {
        struct nfs_client *clp = server->nfs_client;
 
-       if (!nfs4_valid_open_stateid(state))
+       if (!nfs4_state_mark_reclaim_nograce(clp, state))
                return -EBADF;
-       nfs4_state_mark_reclaim_nograce(clp, state);
        dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
                        clp->cl_hostname);
        nfs4_schedule_state_manager(clp);
@@ -1337,6 +1342,35 @@ int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_
 }
 EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery);
 
+static struct nfs4_lock_state *
+nfs_state_find_lock_state_by_stateid(struct nfs4_state *state,
+               const nfs4_stateid *stateid)
+{
+       struct nfs4_lock_state *pos;
+
+       list_for_each_entry(pos, &state->lock_states, ls_locks) {
+               if (!test_bit(NFS_LOCK_INITIALIZED, &pos->ls_flags))
+                       continue;
+               if (nfs4_stateid_match_other(&pos->ls_stateid, stateid))
+                       return pos;
+       }
+       return NULL;
+}
+
+static bool nfs_state_lock_state_matches_stateid(struct nfs4_state *state,
+               const nfs4_stateid *stateid)
+{
+       bool found = false;
+
+       if (test_bit(LK_STATE_IN_USE, &state->flags)) {
+               spin_lock(&state->state_lock);
+               if (nfs_state_find_lock_state_by_stateid(state, stateid))
+                       found = true;
+               spin_unlock(&state->state_lock);
+       }
+       return found;
+}
+
 void nfs_inode_find_state_and_recover(struct inode *inode,
                const nfs4_stateid *stateid)
 {
@@ -1351,14 +1385,18 @@ void nfs_inode_find_state_and_recover(struct inode *inode,
                state = ctx->state;
                if (state == NULL)
                        continue;
-               if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
+               if (nfs4_stateid_match_other(&state->stateid, stateid) &&
+                   nfs4_state_mark_reclaim_nograce(clp, state)) {
+                       found = true;
                        continue;
-               if (!nfs4_stateid_match(&state->stateid, stateid))
-                       continue;
-               nfs4_state_mark_reclaim_nograce(clp, state);
-               found = true;
+               }
+               if (nfs_state_lock_state_matches_stateid(state, stateid) &&
+                   nfs4_state_mark_reclaim_nograce(clp, state))
+                       found = true;
        }
        spin_unlock(&inode->i_lock);
+
+       nfs_inode_find_delegation_state_and_recover(inode, stateid);
        if (found)
                nfs4_schedule_state_manager(clp);
 }
@@ -1498,6 +1536,9 @@ restart:
                                        __func__, status);
                        case -ENOENT:
                        case -ENOMEM:
+                       case -EACCES:
+                       case -EROFS:
+                       case -EIO:
                        case -ESTALE:
                                /* Open state on this file cannot be recovered */
                                nfs4_state_mark_recovery_failed(state, status);
@@ -1656,15 +1697,9 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
        put_rpccred(cred);
 }
 
-static void nfs_delegation_clear_all(struct nfs_client *clp)
-{
-       nfs_delegation_mark_reclaim(clp);
-       nfs_delegation_reap_unclaimed(clp);
-}
-
 static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
 {
-       nfs_delegation_clear_all(clp);
+       nfs_mark_test_expired_all_delegations(clp);
        nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
 }
 
@@ -2195,7 +2230,7 @@ static void nfs41_handle_all_state_revoked(struct nfs_client *clp)
 
 static void nfs41_handle_some_state_revoked(struct nfs_client *clp)
 {
-       nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
+       nfs4_state_start_reclaim_nograce(clp);
        nfs4_schedule_state_manager(clp);
 
        dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname);
@@ -2227,13 +2262,22 @@ static void nfs41_handle_cb_path_down(struct nfs_client *clp)
                nfs4_schedule_state_manager(clp);
 }
 
-void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
+void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags,
+               bool recovery)
 {
        if (!flags)
                return;
 
        dprintk("%s: \"%s\" (client ID %llx) flags=0x%08x\n",
                __func__, clp->cl_hostname, clp->cl_clientid, flags);
+       /*
+        * If we're called from the state manager thread, then assume we're
+        * already handling the RECLAIM_NEEDED and/or STATE_REVOKED.
+        * Those flags are expected to remain set until we're done
+        * recovering (see RFC5661, section 18.46.3).
+        */
+       if (recovery)
+               goto out_recovery;
 
        if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
                nfs41_handle_server_reboot(clp);
@@ -2246,6 +2290,7 @@ void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
                nfs4_schedule_lease_moved_recovery(clp);
        if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
                nfs41_handle_recallable_state_revoked(clp);
+out_recovery:
        if (flags & SEQ4_STATUS_BACKCHANNEL_FAULT)
                nfs41_handle_backchannel_fault(clp);
        else if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
@@ -2410,6 +2455,13 @@ static void nfs4_state_manager(struct nfs_client *clp)
                        nfs4_state_end_reclaim_reboot(clp);
                }
 
+               /* Detect expired delegations... */
+               if (test_and_clear_bit(NFS4CLNT_DELEGATION_EXPIRED, &clp->cl_state)) {
+                       section = "detect expired delegations";
+                       nfs_reap_expired_delegations(clp);
+                       continue;
+               }
+
                /* Now recover expired state... */
                if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
                        section = "reclaim nograce";
index 7bd3a5c09d3185856e85bbe8c5484fccfee3651f..fc89e5ed07eec90f1a0ef12134c9e29a693ee209 100644 (file)
@@ -1850,7 +1850,7 @@ static void encode_create_session(struct xdr_stream *xdr,
        *p++ = cpu_to_be32(RPC_AUTH_UNIX);                      /* auth_sys */
 
        /* authsys_parms rfc1831 */
-       *p++ = cpu_to_be32(nn->boot_time.tv_nsec);      /* stamp */
+       *p++ = cpu_to_be32(ktime_to_ns(nn->boot_time)); /* stamp */
        p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen);
        *p++ = cpu_to_be32(0);                          /* UID */
        *p++ = cpu_to_be32(0);                          /* GID */
@@ -4725,34 +4725,37 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
 }
 
 /*
- * Decode potentially multiple layout types. Currently we only support
- * one layout driver per file system.
+ * Decode potentially multiple layout types.
  */
-static int decode_first_pnfs_layout_type(struct xdr_stream *xdr,
-                                        uint32_t *layouttype)
+static int decode_pnfs_layout_types(struct xdr_stream *xdr,
+                                   struct nfs_fsinfo *fsinfo)
 {
        __be32 *p;
-       int num;
+       uint32_t i;
 
        p = xdr_inline_decode(xdr, 4);
        if (unlikely(!p))
                goto out_overflow;
-       num = be32_to_cpup(p);
+       fsinfo->nlayouttypes = be32_to_cpup(p);
 
        /* pNFS is not supported by the underlying file system */
-       if (num == 0) {
-               *layouttype = 0;
+       if (fsinfo->nlayouttypes == 0)
                return 0;
-       }
-       if (num > 1)
-               printk(KERN_INFO "NFS: %s: Warning: Multiple pNFS layout "
-                       "drivers per filesystem not supported\n", __func__);
 
        /* Decode and set first layout type, move xdr->p past unused types */
-       p = xdr_inline_decode(xdr, num * 4);
+       p = xdr_inline_decode(xdr, fsinfo->nlayouttypes * 4);
        if (unlikely(!p))
                goto out_overflow;
-       *layouttype = be32_to_cpup(p);
+
+       /* If we get too many, then just cap it at the max */
+       if (fsinfo->nlayouttypes > NFS_MAX_LAYOUT_TYPES) {
+               printk(KERN_INFO "NFS: %s: Warning: Too many (%u) pNFS layout types\n",
+                       __func__, fsinfo->nlayouttypes);
+               fsinfo->nlayouttypes = NFS_MAX_LAYOUT_TYPES;
+       }
+
+       for(i = 0; i < fsinfo->nlayouttypes; ++i)
+               fsinfo->layouttype[i] = be32_to_cpup(p++);
        return 0;
 out_overflow:
        print_overflow_msg(__func__, xdr);
@@ -4764,7 +4767,7 @@ out_overflow:
  * Note we must ensure that layouttype is set in any non-error case.
  */
 static int decode_attr_pnfstype(struct xdr_stream *xdr, uint32_t *bitmap,
-                               uint32_t *layouttype)
+                               struct nfs_fsinfo *fsinfo)
 {
        int status = 0;
 
@@ -4772,10 +4775,9 @@ static int decode_attr_pnfstype(struct xdr_stream *xdr, uint32_t *bitmap,
        if (unlikely(bitmap[1] & (FATTR4_WORD1_FS_LAYOUT_TYPES - 1U)))
                return -EIO;
        if (bitmap[1] & FATTR4_WORD1_FS_LAYOUT_TYPES) {
-               status = decode_first_pnfs_layout_type(xdr, layouttype);
+               status = decode_pnfs_layout_types(xdr, fsinfo);
                bitmap[1] &= ~FATTR4_WORD1_FS_LAYOUT_TYPES;
-       } else
-               *layouttype = 0;
+       }
        return status;
 }
 
@@ -4856,7 +4858,7 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
        status = decode_attr_time_delta(xdr, bitmap, &fsinfo->time_delta);
        if (status != 0)
                goto xdr_error;
-       status = decode_attr_pnfstype(xdr, bitmap, &fsinfo->layouttype);
+       status = decode_attr_pnfstype(xdr, bitmap, fsinfo);
        if (status != 0)
                goto xdr_error;
 
index 2c93a85eda51c9d6ebcde892d1e7ca60a1f159b3..56b2d96f9103e42c57e5dd490f2561c8a10c554a 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/nfs_fs.h>
 #include <linux/nfs_page.h>
 #include <linux/module.h>
+#include <linux/sort.h>
 #include "internal.h"
 #include "pnfs.h"
 #include "iostat.h"
@@ -98,36 +99,80 @@ unset_pnfs_layoutdriver(struct nfs_server *nfss)
        nfss->pnfs_curr_ld = NULL;
 }
 
+/*
+ * When the server sends a list of layout types, we choose one in the order
+ * given in the list below.
+ *
+ * FIXME: should this list be configurable in some fashion? module param?
+ *       mount option? something else?
+ */
+static const u32 ld_prefs[] = {
+       LAYOUT_SCSI,
+       LAYOUT_BLOCK_VOLUME,
+       LAYOUT_OSD2_OBJECTS,
+       LAYOUT_FLEX_FILES,
+       LAYOUT_NFSV4_1_FILES,
+       0
+};
+
+static int
+ld_cmp(const void *e1, const void *e2)
+{
+       u32 ld1 = *((u32 *)e1);
+       u32 ld2 = *((u32 *)e2);
+       int i;
+
+       for (i = 0; ld_prefs[i] != 0; i++) {
+               if (ld1 == ld_prefs[i])
+                       return -1;
+
+               if (ld2 == ld_prefs[i])
+                       return 1;
+       }
+       return 0;
+}
+
 /*
  * Try to set the server's pnfs module to the pnfs layout type specified by id.
  * Currently only one pNFS layout driver per filesystem is supported.
  *
- * @id layout type. Zero (illegal layout type) indicates pNFS not in use.
+ * @ids array of layout types supported by MDS.
  */
 void
 set_pnfs_layoutdriver(struct nfs_server *server, const struct nfs_fh *mntfh,
-                     u32 id)
+                     struct nfs_fsinfo *fsinfo)
 {
        struct pnfs_layoutdriver_type *ld_type = NULL;
+       u32 id;
+       int i;
 
-       if (id == 0)
-               goto out_no_driver;
        if (!(server->nfs_client->cl_exchange_flags &
                 (EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS))) {
-               printk(KERN_ERR "NFS: %s: id %u cl_exchange_flags 0x%x\n",
-                       __func__, id, server->nfs_client->cl_exchange_flags);
+               printk(KERN_ERR "NFS: %s: cl_exchange_flags 0x%x\n",
+                       __func__, server->nfs_client->cl_exchange_flags);
                goto out_no_driver;
        }
-       ld_type = find_pnfs_driver(id);
-       if (!ld_type) {
-               request_module("%s-%u", LAYOUT_NFSV4_1_MODULE_PREFIX, id);
+
+       sort(fsinfo->layouttype, fsinfo->nlayouttypes,
+               sizeof(*fsinfo->layouttype), ld_cmp, NULL);
+
+       for (i = 0; i < fsinfo->nlayouttypes; i++) {
+               id = fsinfo->layouttype[i];
                ld_type = find_pnfs_driver(id);
                if (!ld_type) {
-                       dprintk("%s: No pNFS module found for %u.\n",
-                               __func__, id);
-                       goto out_no_driver;
+                       request_module("%s-%u", LAYOUT_NFSV4_1_MODULE_PREFIX,
+                                       id);
+                       ld_type = find_pnfs_driver(id);
                }
+               if (ld_type)
+                       break;
+       }
+
+       if (!ld_type) {
+               dprintk("%s: No pNFS module found!\n", __func__);
+               goto out_no_driver;
        }
+
        server->pnfs_curr_ld = ld_type;
        if (ld_type->set_layoutdriver
            && ld_type->set_layoutdriver(server, mntfh)) {
@@ -2185,10 +2230,8 @@ static void pnfs_ld_handle_read_error(struct nfs_pgio_header *hdr)
  */
 void pnfs_ld_read_done(struct nfs_pgio_header *hdr)
 {
-       if (likely(!hdr->pnfs_error)) {
-               __nfs4_read_done_cb(hdr);
+       if (likely(!hdr->pnfs_error))
                hdr->mds_ops->rpc_call_done(&hdr->task, hdr);
-       }
        trace_nfs4_pnfs_read(hdr, hdr->pnfs_error);
        if (unlikely(hdr->pnfs_error))
                pnfs_ld_handle_read_error(hdr);
index 31d99b2927b000a7d95afebc4df1ad4478c25096..5c295512c9671e4996136264ec8a848077d835ef 100644 (file)
@@ -236,7 +236,7 @@ void pnfs_get_layout_hdr(struct pnfs_layout_hdr *lo);
 void pnfs_put_lseg(struct pnfs_layout_segment *lseg);
 void pnfs_put_lseg_locked(struct pnfs_layout_segment *lseg);
 
-void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, u32);
+void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, struct nfs_fsinfo *);
 void unset_pnfs_layoutdriver(struct nfs_server *);
 void pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *, struct nfs_page *);
 int pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc);
@@ -657,7 +657,8 @@ pnfs_wait_on_layoutreturn(struct inode *ino, struct rpc_task *task)
 }
 
 static inline void set_pnfs_layoutdriver(struct nfs_server *s,
-                                        const struct nfs_fh *mntfh, u32 id)
+                                        const struct nfs_fh *mntfh,
+                                        struct nfs_fsinfo *fsinfo)
 {
 }
 
index f3468b57a32a32c71d7aa55fbf835ae064c16bcc..53b4705abcc76144fc7fb9f1f08a23add0f4df86 100644 (file)
@@ -690,13 +690,50 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
                dprintk("%s: DS %s: trying address %s\n",
                        __func__, ds->ds_remotestr, da->da_remotestr);
 
-               clp = nfs4_set_ds_client(mds_srv,
-                                       (struct sockaddr *)&da->da_addr,
-                                       da->da_addrlen, IPPROTO_TCP,
-                                       timeo, retrans, minor_version,
-                                       au_flavor);
-               if (!IS_ERR(clp))
-                       break;
+               if (!IS_ERR(clp) && clp->cl_mvops->session_trunk) {
+                       struct xprt_create xprt_args = {
+                               .ident = XPRT_TRANSPORT_TCP,
+                               .net = clp->cl_net,
+                               .dstaddr = (struct sockaddr *)&da->da_addr,
+                               .addrlen = da->da_addrlen,
+                               .servername = clp->cl_hostname,
+                       };
+                       struct nfs4_add_xprt_data xprtdata = {
+                               .clp = clp,
+                               .cred = nfs4_get_clid_cred(clp),
+                       };
+                       struct rpc_add_xprt_test rpcdata = {
+                               .add_xprt_test = clp->cl_mvops->session_trunk,
+                               .data = &xprtdata,
+                       };
+
+                       /**
+                       * Test this address for session trunking and
+                       * add as an alias
+                       */
+                       rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
+                                         rpc_clnt_setup_test_and_add_xprt,
+                                         &rpcdata);
+                       if (xprtdata.cred)
+                               put_rpccred(xprtdata.cred);
+               } else {
+                       clp = nfs4_set_ds_client(mds_srv,
+                                               (struct sockaddr *)&da->da_addr,
+                                               da->da_addrlen, IPPROTO_TCP,
+                                               timeo, retrans, minor_version,
+                                               au_flavor);
+                       if (IS_ERR(clp))
+                               continue;
+
+                       status = nfs4_init_ds_session(clp,
+                                       mds_srv->nfs_client->cl_lease_time);
+                       if (status) {
+                               nfs_put_client(clp);
+                               clp = ERR_PTR(-EIO);
+                               continue;
+                       }
+
+               }
        }
 
        if (IS_ERR(clp)) {
@@ -704,18 +741,11 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
                goto out;
        }
 
-       status = nfs4_init_ds_session(clp, mds_srv->nfs_client->cl_lease_time);
-       if (status)
-               goto out_put;
-
        smp_wmb();
        ds->ds_clp = clp;
        dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
 out:
        return status;
-out_put:
-       nfs_put_client(clp);
-       goto out;
 }
 
 /*
index d39601381adf56fe21cd531d09b77f4960cf5060..001796bcd6c895a49e3dd277a0862545fd2205a5 100644 (file)
@@ -2848,19 +2848,23 @@ out_invalid_transport_udp:
  * NFS client for backwards compatibility
  */
 unsigned int nfs_callback_set_tcpport;
+unsigned short nfs_callback_nr_threads;
 /* Default cache timeout is 10 minutes */
 unsigned int nfs_idmap_cache_timeout = 600;
 /* Turn off NFSv4 uid/gid mapping when using AUTH_SYS */
 bool nfs4_disable_idmapping = true;
 unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE;
+unsigned short max_session_cb_slots = NFS4_DEF_CB_SLOT_TABLE_SIZE;
 unsigned short send_implementation_id = 1;
 char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN] = "";
 bool recover_lost_locks = false;
 
+EXPORT_SYMBOL_GPL(nfs_callback_nr_threads);
 EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport);
 EXPORT_SYMBOL_GPL(nfs_idmap_cache_timeout);
 EXPORT_SYMBOL_GPL(nfs4_disable_idmapping);
 EXPORT_SYMBOL_GPL(max_session_slots);
+EXPORT_SYMBOL_GPL(max_session_cb_slots);
 EXPORT_SYMBOL_GPL(send_implementation_id);
 EXPORT_SYMBOL_GPL(nfs4_client_id_uniquifier);
 EXPORT_SYMBOL_GPL(recover_lost_locks);
@@ -2887,6 +2891,9 @@ static const struct kernel_param_ops param_ops_portnr = {
 #define param_check_portnr(name, p) __param_check(name, p, unsigned int);
 
 module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
+module_param_named(callback_nr_threads, nfs_callback_nr_threads, ushort, 0644);
+MODULE_PARM_DESC(callback_nr_threads, "Number of threads that will be "
+               "assigned to the NFSv4 callback channels.");
 module_param(nfs_idmap_cache_timeout, int, 0644);
 module_param(nfs4_disable_idmapping, bool, 0644);
 module_param_string(nfs4_unique_id, nfs4_client_id_uniquifier,
@@ -2896,6 +2903,9 @@ MODULE_PARM_DESC(nfs4_disable_idmapping,
 module_param(max_session_slots, ushort, 0644);
 MODULE_PARM_DESC(max_session_slots, "Maximum number of outstanding NFSv4.1 "
                "requests the client will negotiate");
+module_param(max_session_cb_slots, ushort, 0644);
+MODULE_PARM_DESC(max_session_slots, "Maximum number of parallel NFSv4.1 "
+               "callbacks the client will process for a given server");
 module_param(send_implementation_id, ushort, 0644);
 MODULE_PARM_DESC(send_implementation_id,
                "Send implementation ID with NFSv4.1 exchange_id");
index df880e9fa71fb60d20fe12f82ebb7ed1011ce19c..b672873830109e1d4705a4de7496a23da4227610 100644 (file)
@@ -126,6 +126,7 @@ nfsd4_ff_proc_getdeviceinfo(struct super_block *sb, struct svc_rqst *rqstp,
 const struct nfsd4_layout_ops ff_layout_ops = {
        .notify_types           =
                        NOTIFY_DEVICEID4_DELETE | NOTIFY_DEVICEID4_CHANGE,
+       .disable_recalls        = true,
        .proc_getdeviceinfo     = nfsd4_ff_proc_getdeviceinfo,
        .encode_getdeviceinfo   = nfsd4_ff_encode_getdeviceinfo,
        .proc_layoutget         = nfsd4_ff_proc_layoutget,
index 5fbf3bbd00d04656f1a459cc6c4027989fbb977e..b10d557f9c9ef0033a6dcbcfcf30141745132e8f 100644 (file)
@@ -84,6 +84,7 @@ struct nfsd_net {
        struct list_head client_lru;
        struct list_head close_lru;
        struct list_head del_recall_lru;
+       struct list_head blocked_locks_lru;
 
        struct delayed_work laundromat_work;
 
index 04c68d9003249f884a0d4e49467873c8d75ed9a9..211dc2aed8e182934a81f05090f5319bd11a7608 100644 (file)
@@ -448,7 +448,7 @@ static int decode_cb_sequence4res(struct xdr_stream *xdr,
 {
        int status;
 
-       if (cb->cb_minorversion == 0)
+       if (cb->cb_clp->cl_minorversion == 0)
                return 0;
 
        status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &cb->cb_seq_status);
@@ -485,7 +485,7 @@ static void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr,
        const struct nfs4_delegation *dp = cb_to_delegation(cb);
        struct nfs4_cb_compound_hdr hdr = {
                .ident = cb->cb_clp->cl_cb_ident,
-               .minorversion = cb->cb_minorversion,
+               .minorversion = cb->cb_clp->cl_minorversion,
        };
 
        encode_cb_compound4args(xdr, &hdr);
@@ -594,7 +594,7 @@ static void nfs4_xdr_enc_cb_layout(struct rpc_rqst *req,
                container_of(cb, struct nfs4_layout_stateid, ls_recall);
        struct nfs4_cb_compound_hdr hdr = {
                .ident = 0,
-               .minorversion = cb->cb_minorversion,
+               .minorversion = cb->cb_clp->cl_minorversion,
        };
 
        encode_cb_compound4args(xdr, &hdr);
@@ -623,6 +623,62 @@ static int nfs4_xdr_dec_cb_layout(struct rpc_rqst *rqstp,
 }
 #endif /* CONFIG_NFSD_PNFS */
 
+static void encode_stateowner(struct xdr_stream *xdr, struct nfs4_stateowner *so)
+{
+       __be32  *p;
+
+       p = xdr_reserve_space(xdr, 8 + 4 + so->so_owner.len);
+       p = xdr_encode_opaque_fixed(p, &so->so_client->cl_clientid, 8);
+       xdr_encode_opaque(p, so->so_owner.data, so->so_owner.len);
+}
+
+static void nfs4_xdr_enc_cb_notify_lock(struct rpc_rqst *req,
+                                       struct xdr_stream *xdr,
+                                       const struct nfsd4_callback *cb)
+{
+       const struct nfsd4_blocked_lock *nbl =
+               container_of(cb, struct nfsd4_blocked_lock, nbl_cb);
+       struct nfs4_lockowner *lo = (struct nfs4_lockowner *)nbl->nbl_lock.fl_owner;
+       struct nfs4_cb_compound_hdr hdr = {
+               .ident = 0,
+               .minorversion = cb->cb_clp->cl_minorversion,
+       };
+
+       __be32 *p;
+
+       BUG_ON(hdr.minorversion == 0);
+
+       encode_cb_compound4args(xdr, &hdr);
+       encode_cb_sequence4args(xdr, cb, &hdr);
+
+       p = xdr_reserve_space(xdr, 4);
+       *p = cpu_to_be32(OP_CB_NOTIFY_LOCK);
+       encode_nfs_fh4(xdr, &nbl->nbl_fh);
+       encode_stateowner(xdr, &lo->lo_owner);
+       hdr.nops++;
+
+       encode_cb_nops(&hdr);
+}
+
+static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
+                                       struct xdr_stream *xdr,
+                                       struct nfsd4_callback *cb)
+{
+       struct nfs4_cb_compound_hdr hdr;
+       int status;
+
+       status = decode_cb_compound4res(xdr, &hdr);
+       if (unlikely(status))
+               return status;
+
+       if (cb) {
+               status = decode_cb_sequence4res(xdr, cb);
+               if (unlikely(status || cb->cb_seq_status))
+                       return status;
+       }
+       return decode_cb_op_status(xdr, OP_CB_NOTIFY_LOCK, &cb->cb_status);
+}
+
 /*
  * RPC procedure tables
  */
@@ -643,6 +699,7 @@ static struct rpc_procinfo nfs4_cb_procedures[] = {
 #ifdef CONFIG_NFSD_PNFS
        PROC(CB_LAYOUT, COMPOUND,       cb_layout,      cb_layout),
 #endif
+       PROC(CB_NOTIFY_LOCK,    COMPOUND,       cb_notify_lock, cb_notify_lock),
 };
 
 static struct rpc_version nfs_cb_version4 = {
@@ -862,7 +919,6 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
        struct nfs4_client *clp = cb->cb_clp;
        u32 minorversion = clp->cl_minorversion;
 
-       cb->cb_minorversion = minorversion;
        /*
         * cb_seq_status is only set in decode_cb_sequence4res,
         * and so will remain 1 if an rpc level failure occurs.
index 2be9602b0221bd19f7492dad6266f3fd78c4e050..42aace4fc4c807ec9f41e590e777f8f8f94d7803 100644 (file)
@@ -174,7 +174,8 @@ nfsd4_free_layout_stateid(struct nfs4_stid *stid)
        list_del_init(&ls->ls_perfile);
        spin_unlock(&fp->fi_lock);
 
-       vfs_setlease(ls->ls_file, F_UNLCK, NULL, (void **)&ls);
+       if (!nfsd4_layout_ops[ls->ls_layout_type]->disable_recalls)
+               vfs_setlease(ls->ls_file, F_UNLCK, NULL, (void **)&ls);
        fput(ls->ls_file);
 
        if (ls->ls_recalled)
@@ -189,6 +190,9 @@ nfsd4_layout_setlease(struct nfs4_layout_stateid *ls)
        struct file_lock *fl;
        int status;
 
+       if (nfsd4_layout_ops[ls->ls_layout_type]->disable_recalls)
+               return 0;
+
        fl = locks_alloc_lock();
        if (!fl)
                return -ENOMEM;
index 1fb222752b2b154d1c7171b6c1ea766a8472d09e..abb09b580389a5f81c3c0b04425582c21e6364ec 100644 (file)
@@ -1010,46 +1010,96 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 }
 
 static __be32
-nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
-               struct nfsd4_clone *clone)
+nfsd4_verify_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+                 stateid_t *src_stateid, struct file **src,
+                 stateid_t *dst_stateid, struct file **dst)
 {
-       struct file *src, *dst;
        __be32 status;
 
        status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
-                                           &clone->cl_src_stateid, RD_STATE,
-                                           &src, NULL);
+                                           src_stateid, RD_STATE, src, NULL);
        if (status) {
                dprintk("NFSD: %s: couldn't process src stateid!\n", __func__);
                goto out;
        }
 
        status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
-                                           &clone->cl_dst_stateid, WR_STATE,
-                                           &dst, NULL);
+                                           dst_stateid, WR_STATE, dst, NULL);
        if (status) {
                dprintk("NFSD: %s: couldn't process dst stateid!\n", __func__);
                goto out_put_src;
        }
 
        /* fix up for NFS-specific error code */
-       if (!S_ISREG(file_inode(src)->i_mode) ||
-           !S_ISREG(file_inode(dst)->i_mode)) {
+       if (!S_ISREG(file_inode(*src)->i_mode) ||
+           !S_ISREG(file_inode(*dst)->i_mode)) {
                status = nfserr_wrong_type;
                goto out_put_dst;
        }
 
+out:
+       return status;
+out_put_dst:
+       fput(*dst);
+out_put_src:
+       fput(*src);
+       goto out;
+}
+
+static __be32
+nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+               struct nfsd4_clone *clone)
+{
+       struct file *src, *dst;
+       __be32 status;
+
+       status = nfsd4_verify_copy(rqstp, cstate, &clone->cl_src_stateid, &src,
+                                  &clone->cl_dst_stateid, &dst);
+       if (status)
+               goto out;
+
        status = nfsd4_clone_file_range(src, clone->cl_src_pos,
                        dst, clone->cl_dst_pos, clone->cl_count);
 
-out_put_dst:
        fput(dst);
-out_put_src:
        fput(src);
 out:
        return status;
 }
 
+static __be32
+nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+               struct nfsd4_copy *copy)
+{
+       struct file *src, *dst;
+       __be32 status;
+       ssize_t bytes;
+
+       status = nfsd4_verify_copy(rqstp, cstate, &copy->cp_src_stateid, &src,
+                                  &copy->cp_dst_stateid, &dst);
+       if (status)
+               goto out;
+
+       bytes = nfsd_copy_file_range(src, copy->cp_src_pos,
+                       dst, copy->cp_dst_pos, copy->cp_count);
+
+       if (bytes < 0)
+               status = nfserrno(bytes);
+       else {
+               copy->cp_res.wr_bytes_written = bytes;
+               copy->cp_res.wr_stable_how = NFS_UNSTABLE;
+               copy->cp_consecutive = 1;
+               copy->cp_synchronous = 1;
+               gen_boot_verifier(&copy->cp_res.wr_verifier, SVC_NET(rqstp));
+               status = nfs_ok;
+       }
+
+       fput(src);
+       fput(dst);
+out:
+       return status;
+}
+
 static __be32
 nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                struct nfsd4_fallocate *fallocate, int flags)
@@ -1966,6 +2016,18 @@ static inline u32 nfsd4_create_session_rsize(struct svc_rqst *rqstp, struct nfsd
                op_encode_channel_attrs_maxsz) * sizeof(__be32);
 }
 
+static inline u32 nfsd4_copy_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
+{
+       return (op_encode_hdr_size +
+               1 /* wr_callback */ +
+               op_encode_stateid_maxsz /* wr_callback */ +
+               2 /* wr_count */ +
+               1 /* wr_committed */ +
+               op_encode_verifier_maxsz +
+               1 /* cr_consecutive */ +
+               1 /* cr_synchronous */) * sizeof(__be32);
+}
+
 #ifdef CONFIG_NFSD_PNFS
 /*
  * At this stage we don't really know what layout driver will handle the request,
@@ -2328,6 +2390,12 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_name = "OP_CLONE",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
        },
+       [OP_COPY] = {
+               .op_func = (nfsd4op_func)nfsd4_copy,
+               .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
+               .op_name = "OP_COPY",
+               .op_rsize_bop = (nfsd4op_rsize)nfsd4_copy_rsize,
+       },
        [OP_SEEK] = {
                .op_func = (nfsd4op_func)nfsd4_seek,
                .op_name = "OP_SEEK",
index 39bfaba9c99c932a3ea8cf7005cb014d4dcd175f..9752beb78659dd1f02a4411a397fb27b9a8cf4ef 100644 (file)
@@ -99,6 +99,7 @@ static struct kmem_cache *odstate_slab;
 static void free_session(struct nfsd4_session *);
 
 static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
+static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
 
 static bool is_session_dead(struct nfsd4_session *ses)
 {
@@ -210,6 +211,85 @@ static void nfsd4_put_session(struct nfsd4_session *ses)
        spin_unlock(&nn->client_lock);
 }
 
+static struct nfsd4_blocked_lock *
+find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
+                       struct nfsd_net *nn)
+{
+       struct nfsd4_blocked_lock *cur, *found = NULL;
+
+       spin_lock(&nn->client_lock);
+       list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
+               if (fh_match(fh, &cur->nbl_fh)) {
+                       list_del_init(&cur->nbl_list);
+                       list_del_init(&cur->nbl_lru);
+                       found = cur;
+                       break;
+               }
+       }
+       spin_unlock(&nn->client_lock);
+       if (found)
+               posix_unblock_lock(&found->nbl_lock);
+       return found;
+}
+
+static struct nfsd4_blocked_lock *
+find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
+                       struct nfsd_net *nn)
+{
+       struct nfsd4_blocked_lock *nbl;
+
+       nbl = find_blocked_lock(lo, fh, nn);
+       if (!nbl) {
+               nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
+               if (nbl) {
+                       fh_copy_shallow(&nbl->nbl_fh, fh);
+                       locks_init_lock(&nbl->nbl_lock);
+                       nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
+                                       &nfsd4_cb_notify_lock_ops,
+                                       NFSPROC4_CLNT_CB_NOTIFY_LOCK);
+               }
+       }
+       return nbl;
+}
+
+static void
+free_blocked_lock(struct nfsd4_blocked_lock *nbl)
+{
+       locks_release_private(&nbl->nbl_lock);
+       kfree(nbl);
+}
+
+static int
+nfsd4_cb_notify_lock_done(struct nfsd4_callback *cb, struct rpc_task *task)
+{
+       /*
+        * Since this is just an optimization, we don't try very hard if it
+        * turns out not to succeed. We'll requeue it on NFS4ERR_DELAY, and
+        * just quit trying on anything else.
+        */
+       switch (task->tk_status) {
+       case -NFS4ERR_DELAY:
+               rpc_delay(task, 1 * HZ);
+               return 0;
+       default:
+               return 1;
+       }
+}
+
+static void
+nfsd4_cb_notify_lock_release(struct nfsd4_callback *cb)
+{
+       struct nfsd4_blocked_lock       *nbl = container_of(cb,
+                                               struct nfsd4_blocked_lock, nbl_cb);
+
+       free_blocked_lock(nbl);
+}
+
+static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops = {
+       .done           = nfsd4_cb_notify_lock_done,
+       .release        = nfsd4_cb_notify_lock_release,
+};
+
 static inline struct nfs4_stateowner *
 nfs4_get_stateowner(struct nfs4_stateowner *sop)
 {
@@ -3224,9 +3304,10 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                goto out;
        /* cases below refer to rfc 3530 section 14.2.34: */
        if (!unconf || !same_verf(&confirm, &unconf->cl_confirm)) {
-               if (conf && !unconf) /* case 2: probable retransmit */
+               if (conf && same_verf(&confirm, &conf->cl_confirm)) {
+                       /* case 2: probable retransmit */
                        status = nfs_ok;
-               else /* case 4: client hasn't noticed we rebooted yet? */
+               else /* case 4: client hasn't noticed we rebooted yet? */
                        status = nfserr_stale_clientid;
                goto out;
        }
@@ -4410,9 +4491,11 @@ out:
        * To finish the open response, we just need to set the rflags.
        */
        open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX;
-       if (!(open->op_openowner->oo_flags & NFS4_OO_CONFIRMED) &&
-           !nfsd4_has_session(&resp->cstate))
+       if (nfsd4_has_session(&resp->cstate))
+               open->op_rflags |= NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK;
+       else if (!(open->op_openowner->oo_flags & NFS4_OO_CONFIRMED))
                open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM;
+
        if (dp)
                nfs4_put_stid(&dp->dl_stid);
        if (stp)
@@ -4501,6 +4584,7 @@ nfs4_laundromat(struct nfsd_net *nn)
        struct nfs4_openowner *oo;
        struct nfs4_delegation *dp;
        struct nfs4_ol_stateid *stp;
+       struct nfsd4_blocked_lock *nbl;
        struct list_head *pos, *next, reaplist;
        time_t cutoff = get_seconds() - nn->nfsd4_lease;
        time_t t, new_timeo = nn->nfsd4_lease;
@@ -4569,6 +4653,41 @@ nfs4_laundromat(struct nfsd_net *nn)
        }
        spin_unlock(&nn->client_lock);
 
+       /*
+        * It's possible for a client to try and acquire an already held lock
+        * that is being held for a long time, and then lose interest in it.
+        * So, we clean out any un-revisited request after a lease period
+        * under the assumption that the client is no longer interested.
+        *
+        * RFC5661, sec. 9.6 states that the client must not rely on getting
+        * notifications and must continue to poll for locks, even when the
+        * server supports them. Thus this shouldn't lead to clients blocking
+        * indefinitely once the lock does become free.
+        */
+       BUG_ON(!list_empty(&reaplist));
+       spin_lock(&nn->client_lock);
+       while (!list_empty(&nn->blocked_locks_lru)) {
+               nbl = list_first_entry(&nn->blocked_locks_lru,
+                                       struct nfsd4_blocked_lock, nbl_lru);
+               if (time_after((unsigned long)nbl->nbl_time,
+                              (unsigned long)cutoff)) {
+                       t = nbl->nbl_time - cutoff;
+                       new_timeo = min(new_timeo, t);
+                       break;
+               }
+               list_move(&nbl->nbl_lru, &reaplist);
+               list_del_init(&nbl->nbl_list);
+       }
+       spin_unlock(&nn->client_lock);
+
+       while (!list_empty(&reaplist)) {
+               nbl = list_first_entry(&nn->blocked_locks_lru,
+                                       struct nfsd4_blocked_lock, nbl_lru);
+               list_del_init(&nbl->nbl_lru);
+               posix_unblock_lock(&nbl->nbl_lock);
+               free_blocked_lock(nbl);
+       }
+
        new_timeo = max_t(time_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
        return new_timeo;
 }
@@ -5309,7 +5428,31 @@ nfsd4_fl_put_owner(fl_owner_t owner)
                nfs4_put_stateowner(&lo->lo_owner);
 }
 
+static void
+nfsd4_lm_notify(struct file_lock *fl)
+{
+       struct nfs4_lockowner           *lo = (struct nfs4_lockowner *)fl->fl_owner;
+       struct net                      *net = lo->lo_owner.so_client->net;
+       struct nfsd_net                 *nn = net_generic(net, nfsd_net_id);
+       struct nfsd4_blocked_lock       *nbl = container_of(fl,
+                                               struct nfsd4_blocked_lock, nbl_lock);
+       bool queue = false;
+
+       /* An empty list means that something else is going to be using it */
+       spin_lock(&nn->client_lock);
+       if (!list_empty(&nbl->nbl_list)) {
+               list_del_init(&nbl->nbl_list);
+               list_del_init(&nbl->nbl_lru);
+               queue = true;
+       }
+       spin_unlock(&nn->client_lock);
+
+       if (queue)
+               nfsd4_run_cb(&nbl->nbl_cb);
+}
+
 static const struct lock_manager_operations nfsd_posix_mng_ops  = {
+       .lm_notify = nfsd4_lm_notify,
        .lm_get_owner = nfsd4_fl_get_owner,
        .lm_put_owner = nfsd4_fl_put_owner,
 };
@@ -5407,6 +5550,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
        lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp);
        if (!lo)
                return NULL;
+       INIT_LIST_HEAD(&lo->lo_blocked);
        INIT_LIST_HEAD(&lo->lo_owner.so_stateids);
        lo->lo_owner.so_is_open_owner = 0;
        lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
@@ -5588,12 +5732,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        struct nfs4_ol_stateid *open_stp = NULL;
        struct nfs4_file *fp;
        struct file *filp = NULL;
+       struct nfsd4_blocked_lock *nbl = NULL;
        struct file_lock *file_lock = NULL;
        struct file_lock *conflock = NULL;
        __be32 status = 0;
        int lkflg;
        int err;
        bool new = false;
+       unsigned char fl_type;
+       unsigned int fl_flags = FL_POSIX;
        struct net *net = SVC_NET(rqstp);
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
@@ -5658,46 +5805,55 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (!locks_in_grace(net) && lock->lk_reclaim)
                goto out;
 
-       file_lock = locks_alloc_lock();
-       if (!file_lock) {
-               dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
-               status = nfserr_jukebox;
-               goto out;
-       }
-
        fp = lock_stp->st_stid.sc_file;
        switch (lock->lk_type) {
-               case NFS4_READ_LT:
                case NFS4_READW_LT:
+                       if (nfsd4_has_session(cstate))
+                               fl_flags |= FL_SLEEP;
+                       /* Fallthrough */
+               case NFS4_READ_LT:
                        spin_lock(&fp->fi_lock);
                        filp = find_readable_file_locked(fp);
                        if (filp)
                                get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
                        spin_unlock(&fp->fi_lock);
-                       file_lock->fl_type = F_RDLCK;
+                       fl_type = F_RDLCK;
                        break;
-               case NFS4_WRITE_LT:
                case NFS4_WRITEW_LT:
+                       if (nfsd4_has_session(cstate))
+                               fl_flags |= FL_SLEEP;
+                       /* Fallthrough */
+               case NFS4_WRITE_LT:
                        spin_lock(&fp->fi_lock);
                        filp = find_writeable_file_locked(fp);
                        if (filp)
                                get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
                        spin_unlock(&fp->fi_lock);
-                       file_lock->fl_type = F_WRLCK;
+                       fl_type = F_WRLCK;
                        break;
                default:
                        status = nfserr_inval;
                goto out;
        }
+
        if (!filp) {
                status = nfserr_openmode;
                goto out;
        }
 
+       nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
+       if (!nbl) {
+               dprintk("NFSD: %s: unable to allocate block!\n", __func__);
+               status = nfserr_jukebox;
+               goto out;
+       }
+
+       file_lock = &nbl->nbl_lock;
+       file_lock->fl_type = fl_type;
        file_lock->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lock_sop->lo_owner));
        file_lock->fl_pid = current->tgid;
        file_lock->fl_file = filp;
-       file_lock->fl_flags = FL_POSIX;
+       file_lock->fl_flags = fl_flags;
        file_lock->fl_lmops = &nfsd_posix_mng_ops;
        file_lock->fl_start = lock->lk_offset;
        file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
@@ -5710,18 +5866,29 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                goto out;
        }
 
+       if (fl_flags & FL_SLEEP) {
+               nbl->nbl_time = jiffies;
+               spin_lock(&nn->client_lock);
+               list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
+               list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru);
+               spin_unlock(&nn->client_lock);
+       }
+
        err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
-       switch (-err) {
+       switch (err) {
        case 0: /* success! */
                nfs4_inc_and_copy_stateid(&lock->lk_resp_stateid, &lock_stp->st_stid);
                status = 0;
                break;
-       case (EAGAIN):          /* conflock holds conflicting lock */
+       case FILE_LOCK_DEFERRED:
+               nbl = NULL;
+               /* Fallthrough */
+       case -EAGAIN:           /* conflock holds conflicting lock */
                status = nfserr_denied;
                dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
                nfs4_set_lock_denied(conflock, &lock->lk_denied);
                break;
-       case (EDEADLK):
+       case -EDEADLK:
                status = nfserr_deadlock;
                break;
        default:
@@ -5730,6 +5897,16 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                break;
        }
 out:
+       if (nbl) {
+               /* dequeue it if we queued it before */
+               if (fl_flags & FL_SLEEP) {
+                       spin_lock(&nn->client_lock);
+                       list_del_init(&nbl->nbl_list);
+                       list_del_init(&nbl->nbl_lru);
+                       spin_unlock(&nn->client_lock);
+               }
+               free_blocked_lock(nbl);
+       }
        if (filp)
                fput(filp);
        if (lock_stp) {
@@ -5753,8 +5930,6 @@ out:
        if (open_stp)
                nfs4_put_stid(&open_stp->st_stid);
        nfsd4_bump_seqid(cstate, status);
-       if (file_lock)
-               locks_free_lock(file_lock);
        if (conflock)
                locks_free_lock(conflock);
        return status;
@@ -6768,6 +6943,7 @@ static int nfs4_state_create_net(struct net *net)
        INIT_LIST_HEAD(&nn->client_lru);
        INIT_LIST_HEAD(&nn->close_lru);
        INIT_LIST_HEAD(&nn->del_recall_lru);
+       INIT_LIST_HEAD(&nn->blocked_locks_lru);
        spin_lock_init(&nn->client_lock);
 
        INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
@@ -6865,6 +7041,7 @@ nfs4_state_shutdown_net(struct net *net)
        struct nfs4_delegation *dp = NULL;
        struct list_head *pos, *next, reaplist;
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+       struct nfsd4_blocked_lock *nbl;
 
        cancel_delayed_work_sync(&nn->laundromat_work);
        locks_end_grace(&nn->nfsd4_manager);
@@ -6885,6 +7062,24 @@ nfs4_state_shutdown_net(struct net *net)
                nfs4_put_stid(&dp->dl_stid);
        }
 
+       BUG_ON(!list_empty(&reaplist));
+       spin_lock(&nn->client_lock);
+       while (!list_empty(&nn->blocked_locks_lru)) {
+               nbl = list_first_entry(&nn->blocked_locks_lru,
+                                       struct nfsd4_blocked_lock, nbl_lru);
+               list_move(&nbl->nbl_lru, &reaplist);
+               list_del_init(&nbl->nbl_list);
+       }
+       spin_unlock(&nn->client_lock);
+
+       while (!list_empty(&reaplist)) {
+               nbl = list_first_entry(&nn->blocked_locks_lru,
+                                       struct nfsd4_blocked_lock, nbl_lru);
+               list_del_init(&nbl->nbl_lru);
+               posix_unblock_lock(&nbl->nbl_lock);
+               free_blocked_lock(nbl);
+       }
+
        nfsd4_client_tracking_exit(net);
        nfs4_state_destroy_net(net);
 }
index 0aa0236a142904c6de5123b87ef96c55043c7f7d..c2d2895a1ec1d222f8e3b9913f65f603164707cb 100644 (file)
@@ -1693,6 +1693,30 @@ nfsd4_decode_clone(struct nfsd4_compoundargs *argp, struct nfsd4_clone *clone)
        DECODE_TAIL;
 }
 
+static __be32
+nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
+{
+       DECODE_HEAD;
+       unsigned int tmp;
+
+       status = nfsd4_decode_stateid(argp, &copy->cp_src_stateid);
+       if (status)
+               return status;
+       status = nfsd4_decode_stateid(argp, &copy->cp_dst_stateid);
+       if (status)
+               return status;
+
+       READ_BUF(8 + 8 + 8 + 4 + 4 + 4);
+       p = xdr_decode_hyper(p, &copy->cp_src_pos);
+       p = xdr_decode_hyper(p, &copy->cp_dst_pos);
+       p = xdr_decode_hyper(p, &copy->cp_count);
+       copy->cp_consecutive = be32_to_cpup(p++);
+       copy->cp_synchronous = be32_to_cpup(p++);
+       tmp = be32_to_cpup(p); /* Source server list not supported */
+
+       DECODE_TAIL;
+}
+
 static __be32
 nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
 {
@@ -1793,7 +1817,7 @@ static nfsd4_dec nfsd4_dec_ops[] = {
 
        /* new operations for NFSv4.2 */
        [OP_ALLOCATE]           = (nfsd4_dec)nfsd4_decode_fallocate,
-       [OP_COPY]               = (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_COPY]               = (nfsd4_dec)nfsd4_decode_copy,
        [OP_COPY_NOTIFY]        = (nfsd4_dec)nfsd4_decode_notsupp,
        [OP_DEALLOCATE]         = (nfsd4_dec)nfsd4_decode_fallocate,
        [OP_IO_ADVISE]          = (nfsd4_dec)nfsd4_decode_notsupp,
@@ -4062,7 +4086,7 @@ nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
        u32 starting_len = xdr->buf->len, needed_len;
        __be32 *p;
 
-       dprintk("%s: err %d\n", __func__, nfserr);
+       dprintk("%s: err %d\n", __func__, be32_to_cpu(nfserr));
        if (nfserr)
                goto out;
 
@@ -4201,6 +4225,41 @@ nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr,
 }
 #endif /* CONFIG_NFSD_PNFS */
 
+static __be32
+nfsd42_encode_write_res(struct nfsd4_compoundres *resp, struct nfsd42_write_res *write)
+{
+       __be32 *p;
+
+       p = xdr_reserve_space(&resp->xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE);
+       if (!p)
+               return nfserr_resource;
+
+       *p++ = cpu_to_be32(0);
+       p = xdr_encode_hyper(p, write->wr_bytes_written);
+       *p++ = cpu_to_be32(write->wr_stable_how);
+       p = xdr_encode_opaque_fixed(p, write->wr_verifier.data,
+                                   NFS4_VERIFIER_SIZE);
+       return nfs_ok;
+}
+
+static __be32
+nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
+                 struct nfsd4_copy *copy)
+{
+       __be32 *p;
+
+       if (!nfserr) {
+               nfserr = nfsd42_encode_write_res(resp, &copy->cp_res);
+               if (nfserr)
+                       return nfserr;
+
+               p = xdr_reserve_space(&resp->xdr, 4 + 4);
+               *p++ = cpu_to_be32(copy->cp_consecutive);
+               *p++ = cpu_to_be32(copy->cp_synchronous);
+       }
+       return nfserr;
+}
+
 static __be32
 nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
                  struct nfsd4_seek *seek)
@@ -4300,7 +4359,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
 
        /* NFSv4.2 operations */
        [OP_ALLOCATE]           = (nfsd4_enc)nfsd4_encode_noop,
-       [OP_COPY]               = (nfsd4_enc)nfsd4_encode_noop,
+       [OP_COPY]               = (nfsd4_enc)nfsd4_encode_copy,
        [OP_COPY_NOTIFY]        = (nfsd4_enc)nfsd4_encode_noop,
        [OP_DEALLOCATE]         = (nfsd4_enc)nfsd4_encode_noop,
        [OP_IO_ADVISE]          = (nfsd4_enc)nfsd4_encode_noop,
index 65ad0165a94f8b0f327861a98a5a46f393b7f452..36b2af931e06d1d1a7c3dc613747a58433350811 100644 (file)
@@ -1216,6 +1216,8 @@ static __net_init int nfsd_init_net(struct net *net)
                goto out_idmap_error;
        nn->nfsd4_lease = 90;   /* default lease time */
        nn->nfsd4_grace = 90;
+       nn->clverifier_counter = prandom_u32();
+       nn->clientid_counter = prandom_u32();
        return 0;
 
 out_idmap_error:
index 08188743db537276614a5c9cb56fb49f981123e3..010aff5c5a79f2e91eaefaa671f77cafdf4c1cb5 100644 (file)
@@ -789,6 +789,7 @@ nfserrno (int errno)
                { nfserr_toosmall, -ETOOSMALL },
                { nfserr_serverfault, -ESERVERFAULT },
                { nfserr_serverfault, -ENFILE },
+               { nfserr_io, -EUCLEAN },
        };
        int     i;
 
@@ -796,7 +797,7 @@ nfserrno (int errno)
                if (nfs_errtbl[i].syserr == errno)
                        return nfs_errtbl[i].nfserr;
        }
-       WARN(1, "nfsd: non-standard errno: %d\n", errno);
+       WARN_ONCE(1, "nfsd: non-standard errno: %d\n", errno);
        return nfserr_io;
 }
 
index 45007acaf364686f58eb5242350e06a9e24e239c..a2b65fc56dd662ee39a9023d322b5a6131cc8d8c 100644 (file)
@@ -366,14 +366,21 @@ static struct notifier_block nfsd_inet6addr_notifier = {
 };
 #endif
 
+/* Only used under nfsd_mutex, so this atomic may be overkill: */
+static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
+
 static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
 {
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
-       unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
+       /* check if the notifier still has clients */
+       if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
+               unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
 #if IS_ENABLED(CONFIG_IPV6)
-       unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
+               unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
 #endif
+       }
+
        /*
         * write_ports can create the server without actually starting
         * any threads--if we get shut down before any threads are
@@ -488,10 +495,13 @@ int nfsd_create_serv(struct net *net)
        }
 
        set_max_drc();
-       register_inetaddr_notifier(&nfsd_inetaddr_notifier);
+       /* check if the notifier is already set */
+       if (atomic_inc_return(&nfsd_notifier_refcount) == 1) {
+               register_inetaddr_notifier(&nfsd_inetaddr_notifier);
 #if IS_ENABLED(CONFIG_IPV6)
-       register_inet6addr_notifier(&nfsd_inet6addr_notifier);
+               register_inet6addr_notifier(&nfsd_inet6addr_notifier);
 #endif
+       }
        do_gettimeofday(&nn->nfssvc_boot);              /* record boot time */
        return 0;
 }
index 0c2a716e87411d6b08258632e966666f34190b82..d27a5aa6002294eb5c3fcb6cf4db5b3e2edacd9b 100644 (file)
@@ -19,6 +19,7 @@ struct nfsd4_deviceid_map {
 
 struct nfsd4_layout_ops {
        u32             notify_types;
+       bool            disable_recalls;
 
        __be32 (*proc_getdeviceinfo)(struct super_block *sb,
                        struct svc_rqst *rqstp,
index b95adf9a15954b02a9a37f165f3fc8ef331ccfb8..c9399366f9dfc73b343d079fbad2dc2127927aae 100644 (file)
@@ -63,7 +63,6 @@ typedef struct {
 
 struct nfsd4_callback {
        struct nfs4_client *cb_clp;
-       u32 cb_minorversion;
        struct rpc_message cb_msg;
        const struct nfsd4_callback_ops *cb_ops;
        struct work_struct cb_work;
@@ -441,11 +440,11 @@ struct nfs4_openowner {
 /*
  * Represents a generic "lockowner". Similar to an openowner. References to it
  * are held by the lock stateids that are created on its behalf. This object is
- * a superset of the nfs4_stateowner struct (or would be if it needed any extra
- * fields).
+ * a superset of the nfs4_stateowner struct.
  */
 struct nfs4_lockowner {
-       struct nfs4_stateowner  lo_owner; /* must be first element */
+       struct nfs4_stateowner  lo_owner;       /* must be first element */
+       struct list_head        lo_blocked;     /* blocked file_locks */
 };
 
 static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so)
@@ -572,6 +571,7 @@ enum nfsd4_cb_op {
        NFSPROC4_CLNT_CB_RECALL,
        NFSPROC4_CLNT_CB_LAYOUT,
        NFSPROC4_CLNT_CB_SEQUENCE,
+       NFSPROC4_CLNT_CB_NOTIFY_LOCK,
 };
 
 /* Returns true iff a is later than b: */
@@ -580,6 +580,20 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
        return (s32)(a->si_generation - b->si_generation) > 0;
 }
 
+/*
+ * When a client tries to get a lock on a file, we set one of these objects
+ * on the blocking lock. When the lock becomes free, we can then issue a
+ * CB_NOTIFY_LOCK to the server.
+ */
+struct nfsd4_blocked_lock {
+       struct list_head        nbl_list;
+       struct list_head        nbl_lru;
+       unsigned long           nbl_time;
+       struct file_lock        nbl_lock;
+       struct knfsd_fh         nbl_fh;
+       struct nfsd4_callback   nbl_cb;
+};
+
 struct nfsd4_compound_state;
 struct nfsd_net;
 
index ff476e654b8f8044b84808b1c92c54055e4ca393..8ca642fe9b21f556d9ddc56079af83f9b8c095b3 100644 (file)
@@ -513,6 +513,22 @@ __be32 nfsd4_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
                        count));
 }
 
+ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
+                            u64 dst_pos, u64 count)
+{
+
+       /*
+        * Limit copy to 4MB to prevent indefinitely blocking an nfsd
+        * thread and client rpc slot.  The choice of 4MB is somewhat
+        * arbitrary.  We might instead base this on r/wsize, or make it
+        * tunable, or use a time instead of a byte limit, or implement
+        * asynchronous copy.  In theory a client could also recognize a
+        * limit like this and pipeline multiple COPY requests.
+        */
+       count = min_t(u64, count, 1 << 22);
+       return vfs_copy_file_range(src, src_pos, dst, dst_pos, count, 0);
+}
+
 __be32 nfsd4_vfs_fallocate(struct svc_rqst *rqstp, struct svc_fh *fhp,
                           struct file *file, loff_t offset, loff_t len,
                           int flags)
index 3cbb1b33777b5219aef4116fb6e07fd83183a8a8..0bf9e7bf5800af3855e3d93aaec194dcbea93ba6 100644 (file)
@@ -96,6 +96,8 @@ __be32                nfsd_symlink(struct svc_rqst *, struct svc_fh *,
                                struct svc_fh *res);
 __be32         nfsd_link(struct svc_rqst *, struct svc_fh *,
                                char *, int, struct svc_fh *);
+ssize_t                nfsd_copy_file_range(struct file *, u64,
+                                    struct file *, u64, u64);
 __be32         nfsd_rename(struct svc_rqst *,
                                struct svc_fh *, char *, int,
                                struct svc_fh *, char *, int);
index beea0c5edc51436cb3525fa0f2cc58463f07a3e0..8fda4abdf3b12c358ffa43bef8870b5b2b057341 100644 (file)
@@ -503,6 +503,28 @@ struct nfsd4_clone {
        u64             cl_count;
 };
 
+struct nfsd42_write_res {
+       u64                     wr_bytes_written;
+       u32                     wr_stable_how;
+       nfs4_verifier           wr_verifier;
+};
+
+struct nfsd4_copy {
+       /* request */
+       stateid_t       cp_src_stateid;
+       stateid_t       cp_dst_stateid;
+       u64             cp_src_pos;
+       u64             cp_dst_pos;
+       u64             cp_count;
+
+       /* both */
+       bool            cp_consecutive;
+       bool            cp_synchronous;
+
+       /* response */
+       struct nfsd42_write_res cp_res;
+};
+
 struct nfsd4_seek {
        /* request */
        stateid_t       seek_stateid;
@@ -568,6 +590,7 @@ struct nfsd4_op {
                struct nfsd4_fallocate          allocate;
                struct nfsd4_fallocate          deallocate;
                struct nfsd4_clone              clone;
+               struct nfsd4_copy               copy;
                struct nfsd4_seek               seek;
        } u;
        struct nfs4_replay *                    replay;
index c47f6fdb111aafbd22112efd3e8bcaedd51aa2b1..49b719dfef95a95cef4937d1d9ca5e7c6bc49078 100644 (file)
 #define NFS4_dec_cb_layout_sz          (cb_compound_dec_hdr_sz  +      \
                                        cb_sequence_dec_sz +            \
                                        op_dec_sz)
+
+#define NFS4_enc_cb_notify_lock_sz     (cb_compound_enc_hdr_sz +        \
+                                       cb_sequence_enc_sz +             \
+                                       2 + 1 +                          \
+                                       XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \
+                                       enc_nfs4_fh_sz)
+#define NFS4_dec_cb_notify_lock_sz     (cb_compound_dec_hdr_sz  +      \
+                                       cb_sequence_dec_sz +            \
+                                       op_dec_sz)
index a7719cfb7257e9b19b2ff50efeb5366f98f4d24b..d3ed8171e8e09291b44524e6de943bc3e71c91e3 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -267,6 +267,11 @@ int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
            (mode & ~FALLOC_FL_INSERT_RANGE))
                return -EINVAL;
 
+       /* Unshare range should only be used with allocate mode. */
+       if ((mode & FALLOC_FL_UNSHARE_RANGE) &&
+           (mode & ~(FALLOC_FL_UNSHARE_RANGE | FALLOC_FL_KEEP_SIZE)))
+               return -EINVAL;
+
        if (!(file->f_mode & FMODE_WRITE))
                return -EBADF;
 
index 3f803b3a1f8295b9d9d3f903cc9f1198c6910f4a..aeb60f791418d6d6cba59f2dc743e8b8c71d5297 100644 (file)
@@ -57,6 +57,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
        ssize_t list_size, size, value_size = 0;
        char *buf, *name, *value = NULL;
        int uninitialized_var(error);
+       size_t slen;
 
        if (!(old->d_inode->i_opflags & IOP_XATTR) ||
            !(new->d_inode->i_opflags & IOP_XATTR))
@@ -79,7 +80,16 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
                goto out;
        }
 
-       for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
+       for (name = buf; list_size; name += slen) {
+               slen = strnlen(name, list_size) + 1;
+
+               /* underlying fs providing us with an broken xattr list? */
+               if (WARN_ON(slen > list_size)) {
+                       error = -EIO;
+                       break;
+               }
+               list_size -= slen;
+
                if (ovl_is_private_xattr(name))
                        continue;
 retry:
@@ -174,40 +184,6 @@ out_fput:
        return error;
 }
 
-static char *ovl_read_symlink(struct dentry *realdentry)
-{
-       int res;
-       char *buf;
-       struct inode *inode = realdentry->d_inode;
-       mm_segment_t old_fs;
-
-       res = -EINVAL;
-       if (!inode->i_op->readlink)
-               goto err;
-
-       res = -ENOMEM;
-       buf = (char *) __get_free_page(GFP_KERNEL);
-       if (!buf)
-               goto err;
-
-       old_fs = get_fs();
-       set_fs(get_ds());
-       /* The cast to a user pointer is valid due to the set_fs() */
-       res = inode->i_op->readlink(realdentry,
-                                   (char __user *)buf, PAGE_SIZE - 1);
-       set_fs(old_fs);
-       if (res < 0) {
-               free_page((unsigned long) buf);
-               goto err;
-       }
-       buf[res] = '\0';
-
-       return buf;
-
-err:
-       return ERR_PTR(res);
-}
-
 static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat)
 {
        struct iattr attr = {
@@ -354,19 +330,20 @@ out_cleanup:
 int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
                    struct path *lowerpath, struct kstat *stat)
 {
+       DEFINE_DELAYED_CALL(done);
        struct dentry *workdir = ovl_workdir(dentry);
        int err;
        struct kstat pstat;
        struct path parentpath;
+       struct dentry *lowerdentry = lowerpath->dentry;
        struct dentry *upperdir;
        struct dentry *upperdentry;
-       const struct cred *old_cred;
-       char *link = NULL;
+       const char *link = NULL;
 
        if (WARN_ON(!workdir))
                return -EROFS;
 
-       ovl_do_check_copy_up(lowerpath->dentry);
+       ovl_do_check_copy_up(lowerdentry);
 
        ovl_path_upper(parent, &parentpath);
        upperdir = parentpath.dentry;
@@ -376,13 +353,11 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
                return err;
 
        if (S_ISLNK(stat->mode)) {
-               link = ovl_read_symlink(lowerpath->dentry);
+               link = vfs_get_link(lowerdentry, &done);
                if (IS_ERR(link))
                        return PTR_ERR(link);
        }
 
-       old_cred = ovl_override_creds(dentry->d_sb);
-
        err = -EIO;
        if (lock_rename(workdir, upperdir) != NULL) {
                pr_err("overlayfs: failed to lock workdir+upperdir\n");
@@ -403,19 +378,16 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
        }
 out_unlock:
        unlock_rename(workdir, upperdir);
-       revert_creds(old_cred);
-
-       if (link)
-               free_page((unsigned long) link);
+       do_delayed_call(&done);
 
        return err;
 }
 
 int ovl_copy_up(struct dentry *dentry)
 {
-       int err;
+       int err = 0;
+       const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
 
-       err = 0;
        while (!err) {
                struct dentry *next;
                struct dentry *parent;
@@ -447,6 +419,7 @@ int ovl_copy_up(struct dentry *dentry)
                dput(parent);
                dput(next);
        }
+       revert_creds(old_cred);
 
        return err;
 }
index 5f90ddf778bab71db8c7b651e64f0c321c15b926..306b6c16184081f26579b25f830b1b6f5eb0c094 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/cred.h>
 #include <linux/posix_acl.h>
 #include <linux/posix_acl_xattr.h>
+#include <linux/atomic.h>
 #include "overlayfs.h"
 
 void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
@@ -37,8 +38,10 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir, struct dentry *dentry)
 {
        struct dentry *temp;
        char name[20];
+       static atomic_t temp_id = ATOMIC_INIT(0);
 
-       snprintf(name, sizeof(name), "#%lx", (unsigned long) dentry);
+       /* counter is allowed to wrap, since temp dentries are ephemeral */
+       snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id));
 
        temp = lookup_one_len(name, workdir, strlen(name));
        if (!IS_ERR(temp) && temp->d_inode) {
index c18d6a4ff456bfd57015eebc0e0c083246d55fb0..c58f01babf30e7ecbfe86ab0862a8c8171734866 100644 (file)
@@ -19,6 +19,7 @@ static int ovl_copy_up_truncate(struct dentry *dentry)
        struct dentry *parent;
        struct kstat stat;
        struct path lowerpath;
+       const struct cred *old_cred;
 
        parent = dget_parent(dentry);
        err = ovl_copy_up(parent);
@@ -26,12 +27,14 @@ static int ovl_copy_up_truncate(struct dentry *dentry)
                goto out_dput_parent;
 
        ovl_path_lower(dentry, &lowerpath);
-       err = vfs_getattr(&lowerpath, &stat);
-       if (err)
-               goto out_dput_parent;
 
-       stat.size = 0;
-       err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat);
+       old_cred = ovl_override_creds(dentry->d_sb);
+       err = vfs_getattr(&lowerpath, &stat);
+       if (!err) {
+               stat.size = 0;
+               err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat);
+       }
+       revert_creds(old_cred);
 
 out_dput_parent:
        dput(parent);
@@ -153,45 +156,18 @@ static const char *ovl_get_link(struct dentry *dentry,
                                struct inode *inode,
                                struct delayed_call *done)
 {
-       struct dentry *realdentry;
-       struct inode *realinode;
        const struct cred *old_cred;
        const char *p;
 
        if (!dentry)
                return ERR_PTR(-ECHILD);
 
-       realdentry = ovl_dentry_real(dentry);
-       realinode = realdentry->d_inode;
-
-       if (WARN_ON(!realinode->i_op->get_link))
-               return ERR_PTR(-EPERM);
-
        old_cred = ovl_override_creds(dentry->d_sb);
-       p = realinode->i_op->get_link(realdentry, realinode, done);
+       p = vfs_get_link(ovl_dentry_real(dentry), done);
        revert_creds(old_cred);
        return p;
 }
 
-static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
-{
-       struct path realpath;
-       struct inode *realinode;
-       const struct cred *old_cred;
-       int err;
-
-       ovl_path_real(dentry, &realpath);
-       realinode = realpath.dentry->d_inode;
-
-       if (!realinode->i_op->readlink)
-               return -EINVAL;
-
-       old_cred = ovl_override_creds(dentry->d_sb);
-       err = realinode->i_op->readlink(realpath.dentry, buf, bufsiz);
-       revert_creds(old_cred);
-       return err;
-}
-
 bool ovl_is_private_xattr(const char *name)
 {
        return strncmp(name, OVL_XATTR_PREFIX,
@@ -375,7 +351,7 @@ static const struct inode_operations ovl_file_inode_operations = {
 static const struct inode_operations ovl_symlink_inode_operations = {
        .setattr        = ovl_setattr,
        .get_link       = ovl_get_link,
-       .readlink       = ovl_readlink,
+       .readlink       = generic_readlink,
        .getattr        = ovl_getattr,
        .listxattr      = ovl_listxattr,
        .update_time    = ovl_update_time,
index 7e3f0127fc1aa7fea2096fcf806bca4dc9c9b60f..bcf3965be81942415c404b1253bfd59f809c0100 100644 (file)
@@ -273,12 +273,11 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
 {
        int res;
        char val;
-       struct inode *inode = dentry->d_inode;
 
-       if (!S_ISDIR(inode->i_mode) || !(inode->i_opflags & IOP_XATTR))
+       if (!d_is_dir(dentry))
                return false;
 
-       res = __vfs_getxattr(dentry, inode, OVL_XATTR_OPAQUE, &val, 1);
+       res = vfs_getxattr(dentry, OVL_XATTR_OPAQUE, &val, 1);
        if (res == 1 && val == 'y')
                return true;
 
@@ -419,16 +418,12 @@ static bool ovl_dentry_weird(struct dentry *dentry)
                                  DCACHE_OP_COMPARE);
 }
 
-static inline struct dentry *ovl_lookup_real(struct super_block *ovl_sb,
-                                            struct dentry *dir,
+static inline struct dentry *ovl_lookup_real(struct dentry *dir,
                                             const struct qstr *name)
 {
-       const struct cred *old_cred;
        struct dentry *dentry;
 
-       old_cred = ovl_override_creds(ovl_sb);
        dentry = lookup_one_len_unlocked(name->name, dir, name->len);
-       revert_creds(old_cred);
 
        if (IS_ERR(dentry)) {
                if (PTR_ERR(dentry) == -ENOENT)
@@ -469,6 +464,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                          unsigned int flags)
 {
        struct ovl_entry *oe;
+       const struct cred *old_cred;
        struct ovl_entry *poe = dentry->d_parent->d_fsdata;
        struct path *stack = NULL;
        struct dentry *upperdir, *upperdentry = NULL;
@@ -479,9 +475,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
        unsigned int i;
        int err;
 
+       old_cred = ovl_override_creds(dentry->d_sb);
        upperdir = ovl_upperdentry_dereference(poe);
        if (upperdir) {
-               this = ovl_lookup_real(dentry->d_sb, upperdir, &dentry->d_name);
+               this = ovl_lookup_real(upperdir, &dentry->d_name);
                err = PTR_ERR(this);
                if (IS_ERR(this))
                        goto out;
@@ -514,8 +511,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                bool opaque = false;
                struct path lowerpath = poe->lowerstack[i];
 
-               this = ovl_lookup_real(dentry->d_sb,
-                                      lowerpath.dentry, &dentry->d_name);
+               this = ovl_lookup_real(lowerpath.dentry, &dentry->d_name);
                err = PTR_ERR(this);
                if (IS_ERR(this)) {
                        /*
@@ -588,6 +584,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                ovl_copyattr(realdentry->d_inode, inode);
        }
 
+       revert_creds(old_cred);
        oe->opaque = upperopaque;
        oe->__upperdentry = upperdentry;
        memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
@@ -606,6 +603,7 @@ out_put:
 out_put_upper:
        dput(upperdentry);
 out:
+       revert_creds(old_cred);
        return ERR_PTR(err);
 }
 
@@ -834,6 +832,19 @@ retry:
                if (err)
                        goto out_dput;
 
+               /*
+                * Try to remove POSIX ACL xattrs from workdir.  We are good if:
+                *
+                * a) success (there was a POSIX ACL xattr and was removed)
+                * b) -ENODATA (there was no POSIX ACL xattr)
+                * c) -EOPNOTSUPP (POSIX ACL xattrs are not supported)
+                *
+                * There are various other error values that could effectively
+                * mean that the xattr doesn't exist (e.g. -ERANGE is returned
+                * if the xattr name is too long), but the set of filesystems
+                * allowed as upper are limited to "normal" ones, where checking
+                * for the above two errors is sufficient.
+                */
                err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_DEFAULT);
                if (err && err != -ENODATA && err != -EOPNOTSUPP)
                        goto out_dput;
@@ -1292,6 +1303,12 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        if (!oe)
                goto out_put_cred;
 
+       sb->s_magic = OVERLAYFS_SUPER_MAGIC;
+       sb->s_op = &ovl_super_operations;
+       sb->s_xattr = ovl_xattr_handlers;
+       sb->s_fs_info = ufs;
+       sb->s_flags |= MS_POSIXACL | MS_NOREMOTELOCK;
+
        root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR));
        if (!root_dentry)
                goto out_free_oe;
@@ -1315,12 +1332,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        ovl_inode_init(d_inode(root_dentry), realinode, !!upperpath.dentry);
        ovl_copyattr(realinode, d_inode(root_dentry));
 
-       sb->s_magic = OVERLAYFS_SUPER_MAGIC;
-       sb->s_op = &ovl_super_operations;
-       sb->s_xattr = ovl_xattr_handlers;
        sb->s_root = root_dentry;
-       sb->s_fs_info = ufs;
-       sb->s_flags |= MS_POSIXACL | MS_NOREMOTELOCK;
 
        return 0;
 
index c2964d890c9a58910d4d9c2b53c5845637fed70e..8e654468ab676900bdcfb029558a766e1b448691 100644 (file)
@@ -252,7 +252,7 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
         * Inherently racy -- command line shares address space
         * with code and data.
         */
-       rv = access_remote_vm(mm, arg_end - 1, &c, 1, 0);
+       rv = access_remote_vm(mm, arg_end - 1, &c, 1, FOLL_FORCE);
        if (rv <= 0)
                goto out_free_page;
 
@@ -270,7 +270,8 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
                        int nr_read;
 
                        _count = min3(count, len, PAGE_SIZE);
-                       nr_read = access_remote_vm(mm, p, page, _count, 0);
+                       nr_read = access_remote_vm(mm, p, page, _count,
+                                       FOLL_FORCE);
                        if (nr_read < 0)
                                rv = nr_read;
                        if (nr_read <= 0)
@@ -305,7 +306,8 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
                        bool final;
 
                        _count = min3(count, len, PAGE_SIZE);
-                       nr_read = access_remote_vm(mm, p, page, _count, 0);
+                       nr_read = access_remote_vm(mm, p, page, _count,
+                                       FOLL_FORCE);
                        if (nr_read < 0)
                                rv = nr_read;
                        if (nr_read <= 0)
@@ -354,7 +356,8 @@ skip_argv:
                        bool final;
 
                        _count = min3(count, len, PAGE_SIZE);
-                       nr_read = access_remote_vm(mm, p, page, _count, 0);
+                       nr_read = access_remote_vm(mm, p, page, _count,
+                                       FOLL_FORCE);
                        if (nr_read < 0)
                                rv = nr_read;
                        if (nr_read <= 0)
@@ -832,6 +835,7 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
        unsigned long addr = *ppos;
        ssize_t copied;
        char *page;
+       unsigned int flags = FOLL_FORCE;
 
        if (!mm)
                return 0;
@@ -844,6 +848,9 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
        if (!atomic_inc_not_zero(&mm->mm_users))
                goto free;
 
+       if (write)
+               flags |= FOLL_WRITE;
+
        while (count > 0) {
                int this_len = min_t(int, count, PAGE_SIZE);
 
@@ -852,7 +859,7 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
                        break;
                }
 
-               this_len = access_remote_vm(mm, addr, page, this_len, write);
+               this_len = access_remote_vm(mm, addr, page, this_len, flags);
                if (!this_len) {
                        if (!copied)
                                copied = -EIO;
@@ -965,7 +972,7 @@ static ssize_t environ_read(struct file *file, char __user *buf,
                this_len = min(max_len, this_len);
 
                retval = access_remote_vm(mm, (env_start + src),
-                       page, this_len, 0);
+                       page, this_len, FOLL_FORCE);
 
                if (retval <= 0) {
                        ret = retval;
index 66215a7b17cf14d0b776dbb8e62b310a1571fdeb..190e0d362581a9a87d5cdae2a069e0d7e8991af9 100644 (file)
@@ -730,6 +730,35 @@ static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter,
 /* A write operation does a read from user space and vice versa */
 #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)
 
+/**
+ * rw_copy_check_uvector() - Copy an array of &struct iovec from userspace
+ *     into the kernel and check that it is valid.
+ *
+ * @type: One of %CHECK_IOVEC_ONLY, %READ, or %WRITE.
+ * @uvector: Pointer to the userspace array.
+ * @nr_segs: Number of elements in userspace array.
+ * @fast_segs: Number of elements in @fast_pointer.
+ * @fast_pointer: Pointer to (usually small on-stack) kernel array.
+ * @ret_pointer: (output parameter) Pointer to a variable that will point to
+ *     either @fast_pointer, a newly allocated kernel array, or NULL,
+ *     depending on which array was used.
+ *
+ * This function copies an array of &struct iovec of @nr_segs from
+ * userspace into the kernel and checks that each element is valid (e.g.
+ * it does not point to a kernel address or cause overflow by being too
+ * large, etc.).
+ *
+ * As an optimization, the caller may provide a pointer to a small
+ * on-stack array in @fast_pointer, typically %UIO_FASTIOV elements long
+ * (the size of this array, or 0 if unused, should be given in @fast_segs).
+ *
+ * @ret_pointer will always point to the array that was used, so the
+ * caller must take care not to call kfree() on it e.g. in case the
+ * @fast_pointer array was used and it was allocated on the stack.
+ *
+ * Return: The total number of bytes covered by the iovec array on success
+ *   or a negative error code on error.
+ */
 ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
                              unsigned long nr_segs, unsigned long fast_segs,
                              struct iovec *fast_pointer,
index c2ff475c1711f38aa0b4b5296c68589e68fa1fc2..c183835566c19c56fd30d6a1c22d380d10668411 100644 (file)
@@ -1269,25 +1269,34 @@ EXPORT_SYMBOL(__sb_start_write);
 static void sb_wait_write(struct super_block *sb, int level)
 {
        percpu_down_write(sb->s_writers.rw_sem + level-1);
-       /*
-        * We are going to return to userspace and forget about this lock, the
-        * ownership goes to the caller of thaw_super() which does unlock.
-        *
-        * FIXME: we should do this before return from freeze_super() after we
-        * called sync_filesystem(sb) and s_op->freeze_fs(sb), and thaw_super()
-        * should re-acquire these locks before s_op->unfreeze_fs(sb). However
-        * this leads to lockdep false-positives, so currently we do the early
-        * release right after acquire.
-        */
-       percpu_rwsem_release(sb->s_writers.rw_sem + level-1, 0, _THIS_IP_);
 }
 
-static void sb_freeze_unlock(struct super_block *sb)
+/*
+ * We are going to return to userspace and forget about these locks, the
+ * ownership goes to the caller of thaw_super() which does unlock().
+ */
+static void lockdep_sb_freeze_release(struct super_block *sb)
+{
+       int level;
+
+       for (level = SB_FREEZE_LEVELS - 1; level >= 0; level--)
+               percpu_rwsem_release(sb->s_writers.rw_sem + level, 0, _THIS_IP_);
+}
+
+/*
+ * Tell lockdep we are holding these locks before we call ->unfreeze_fs(sb).
+ */
+static void lockdep_sb_freeze_acquire(struct super_block *sb)
 {
        int level;
 
        for (level = 0; level < SB_FREEZE_LEVELS; ++level)
                percpu_rwsem_acquire(sb->s_writers.rw_sem + level, 0, _THIS_IP_);
+}
+
+static void sb_freeze_unlock(struct super_block *sb)
+{
+       int level;
 
        for (level = SB_FREEZE_LEVELS - 1; level >= 0; level--)
                percpu_up_write(sb->s_writers.rw_sem + level);
@@ -1379,10 +1388,11 @@ int freeze_super(struct super_block *sb)
                }
        }
        /*
-        * This is just for debugging purposes so that fs can warn if it
-        * sees write activity when frozen is set to SB_FREEZE_COMPLETE.
+        * For debugging purposes so that fs can warn if it sees write activity
+        * when frozen is set to SB_FREEZE_COMPLETE, and for thaw_super().
         */
        sb->s_writers.frozen = SB_FREEZE_COMPLETE;
+       lockdep_sb_freeze_release(sb);
        up_write(&sb->s_umount);
        return 0;
 }
@@ -1399,7 +1409,7 @@ int thaw_super(struct super_block *sb)
        int error;
 
        down_write(&sb->s_umount);
-       if (sb->s_writers.frozen == SB_UNFROZEN) {
+       if (sb->s_writers.frozen != SB_FREEZE_COMPLETE) {
                up_write(&sb->s_umount);
                return -EINVAL;
        }
@@ -1409,11 +1419,14 @@ int thaw_super(struct super_block *sb)
                goto out;
        }
 
+       lockdep_sb_freeze_acquire(sb);
+
        if (sb->s_op->unfreeze_fs) {
                error = sb->s_op->unfreeze_fs(sb);
                if (error) {
                        printk(KERN_ERR
                                "VFS:Filesystem thaw failed\n");
+                       lockdep_sb_freeze_release(sb);
                        up_write(&sb->s_umount);
                        return error;
                }
index 94374e43502599c466153858476bd42652a7ea1b..2b67bda2021b9e2955ae66ba03e536dfdcefec2a 100644 (file)
@@ -21,14 +21,14 @@ DEFINE_SPINLOCK(sysfs_symlink_target_lock);
 
 void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
 {
-       char *buf, *path = NULL;
+       char *buf;
 
        buf = kzalloc(PATH_MAX, GFP_KERNEL);
        if (buf)
-               path = kernfs_path(parent, buf, PATH_MAX);
+               kernfs_path(parent, buf, PATH_MAX);
 
        WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s/%s'\n",
-            path, name);
+            buf, name);
 
        kfree(buf);
 }
index 584e87e11cb6d2e9291defcc50b803d6768e12dc..26ef1958b65b5289583090b6143b2ddec03c8c90 100644 (file)
@@ -55,6 +55,8 @@ xfs-y                         += $(addprefix libxfs/, \
                                   xfs_ag_resv.o \
                                   xfs_rmap.o \
                                   xfs_rmap_btree.o \
+                                  xfs_refcount.o \
+                                  xfs_refcount_btree.o \
                                   xfs_sb.o \
                                   xfs_symlink_remote.o \
                                   xfs_trans_resv.o \
@@ -88,6 +90,7 @@ xfs-y                         += xfs_aops.o \
                                   xfs_message.o \
                                   xfs_mount.o \
                                   xfs_mru_cache.o \
+                                  xfs_reflink.o \
                                   xfs_stats.o \
                                   xfs_super.o \
                                   xfs_symlink.o \
@@ -100,16 +103,20 @@ xfs-y                             += xfs_aops.o \
 # low-level transaction/log code
 xfs-y                          += xfs_log.o \
                                   xfs_log_cil.o \
+                                  xfs_bmap_item.o \
                                   xfs_buf_item.o \
                                   xfs_extfree_item.o \
                                   xfs_icreate_item.o \
                                   xfs_inode_item.o \
+                                  xfs_refcount_item.o \
                                   xfs_rmap_item.o \
                                   xfs_log_recover.o \
                                   xfs_trans_ail.o \
+                                  xfs_trans_bmap.o \
                                   xfs_trans_buf.o \
                                   xfs_trans_extfree.o \
                                   xfs_trans_inode.o \
+                                  xfs_trans_refcount.o \
                                   xfs_trans_rmap.o \
 
 # optional features
index e3ae0f2b4294cae7cb8adec79886b18ba6fe34dd..e5ebc37704608a336dd8690d55f06389cd0173bd 100644 (file)
@@ -38,6 +38,7 @@
 #include "xfs_trans_space.h"
 #include "xfs_rmap_btree.h"
 #include "xfs_btree.h"
+#include "xfs_refcount_btree.h"
 
 /*
  * Per-AG Block Reservations
@@ -108,7 +109,9 @@ xfs_ag_resv_critical(
        trace_xfs_ag_resv_critical(pag, type, avail);
 
        /* Critically low if less than 10% or max btree height remains. */
-       return avail < orig / 10 || avail < XFS_BTREE_MAXLEVELS;
+       return XFS_TEST_ERROR(avail < orig / 10 || avail < XFS_BTREE_MAXLEVELS,
+                       pag->pag_mount, XFS_ERRTAG_AG_RESV_CRITICAL,
+                       XFS_RANDOM_AG_RESV_CRITICAL);
 }
 
 /*
@@ -228,6 +231,11 @@ xfs_ag_resv_init(
        if (pag->pag_meta_resv.ar_asked == 0) {
                ask = used = 0;
 
+               error = xfs_refcountbt_calc_reserves(pag->pag_mount,
+                               pag->pag_agno, &ask, &used);
+               if (error)
+                       goto out;
+
                error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,
                                ask, used);
                if (error)
@@ -238,6 +246,11 @@ xfs_ag_resv_init(
        if (pag->pag_agfl_resv.ar_asked == 0) {
                ask = used = 0;
 
+               error = xfs_rmapbt_calc_reserves(pag->pag_mount, pag->pag_agno,
+                               &ask, &used);
+               if (error)
+                       goto out;
+
                error = __xfs_ag_resv_init(pag, XFS_AG_RESV_AGFL, ask, used);
                if (error)
                        goto out;
index ca75dc90ebe0152a6f6d215d77d4b29ff6435e02..effb64cf714fee3894821a6d6796b3affd583faa 100644 (file)
@@ -52,10 +52,23 @@ STATIC int xfs_alloc_ag_vextent_size(xfs_alloc_arg_t *);
 STATIC int xfs_alloc_ag_vextent_small(xfs_alloc_arg_t *,
                xfs_btree_cur_t *, xfs_agblock_t *, xfs_extlen_t *, int *);
 
+unsigned int
+xfs_refc_block(
+       struct xfs_mount        *mp)
+{
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+               return XFS_RMAP_BLOCK(mp) + 1;
+       if (xfs_sb_version_hasfinobt(&mp->m_sb))
+               return XFS_FIBT_BLOCK(mp) + 1;
+       return XFS_IBT_BLOCK(mp) + 1;
+}
+
 xfs_extlen_t
 xfs_prealloc_blocks(
        struct xfs_mount        *mp)
 {
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               return xfs_refc_block(mp) + 1;
        if (xfs_sb_version_hasrmapbt(&mp->m_sb))
                return XFS_RMAP_BLOCK(mp) + 1;
        if (xfs_sb_version_hasfinobt(&mp->m_sb))
@@ -115,6 +128,8 @@ xfs_alloc_ag_max_usable(
                blocks++;               /* finobt root block */
        if (xfs_sb_version_hasrmapbt(&mp->m_sb))
                blocks++;               /* rmap root block */
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               blocks++;               /* refcount root block */
 
        return mp->m_sb.sb_agblocks - blocks;
 }
@@ -2321,6 +2336,9 @@ xfs_alloc_log_agf(
                offsetof(xfs_agf_t, agf_btreeblks),
                offsetof(xfs_agf_t, agf_uuid),
                offsetof(xfs_agf_t, agf_rmap_blocks),
+               offsetof(xfs_agf_t, agf_refcount_blocks),
+               offsetof(xfs_agf_t, agf_refcount_root),
+               offsetof(xfs_agf_t, agf_refcount_level),
                /* needed so that we don't log the whole rest of the structure: */
                offsetof(xfs_agf_t, agf_spare64),
                sizeof(xfs_agf_t)
@@ -2458,6 +2476,10 @@ xfs_agf_verify(
            be32_to_cpu(agf->agf_btreeblks) > be32_to_cpu(agf->agf_length))
                return false;
 
+       if (xfs_sb_version_hasreflink(&mp->m_sb) &&
+           be32_to_cpu(agf->agf_refcount_level) > XFS_BTREE_MAXLEVELS)
+               return false;
+
        return true;;
 
 }
@@ -2578,6 +2600,7 @@ xfs_alloc_read_agf(
                        be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNTi]);
                pag->pagf_levels[XFS_BTNUM_RMAPi] =
                        be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAPi]);
+               pag->pagf_refcount_level = be32_to_cpu(agf->agf_refcount_level);
                spin_lock_init(&pag->pagb_lock);
                pag->pagb_count = 0;
                pag->pagb_tree = RB_ROOT;
index 9d7f61d36645f828b8267a57131fb1f212788d72..c27344cf38e177187f048eb799297f281eea5a2f 100644 (file)
@@ -48,6 +48,7 @@
 #include "xfs_filestream.h"
 #include "xfs_rmap.h"
 #include "xfs_ag_resv.h"
+#include "xfs_refcount.h"
 
 
 kmem_zone_t            *xfs_bmap_free_item_zone;
@@ -140,7 +141,8 @@ xfs_bmbt_lookup_ge(
  */
 static inline bool xfs_bmap_needs_btree(struct xfs_inode *ip, int whichfork)
 {
-       return XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS &&
+       return whichfork != XFS_COW_FORK &&
+               XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS &&
                XFS_IFORK_NEXTENTS(ip, whichfork) >
                        XFS_IFORK_MAXEXT(ip, whichfork);
 }
@@ -150,7 +152,8 @@ static inline bool xfs_bmap_needs_btree(struct xfs_inode *ip, int whichfork)
  */
 static inline bool xfs_bmap_wants_extents(struct xfs_inode *ip, int whichfork)
 {
-       return XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE &&
+       return whichfork != XFS_COW_FORK &&
+               XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE &&
                XFS_IFORK_NEXTENTS(ip, whichfork) <=
                        XFS_IFORK_MAXEXT(ip, whichfork);
 }
@@ -640,6 +643,7 @@ xfs_bmap_btree_to_extents(
 
        mp = ip->i_mount;
        ifp = XFS_IFORK_PTR(ip, whichfork);
+       ASSERT(whichfork != XFS_COW_FORK);
        ASSERT(ifp->if_flags & XFS_IFEXTENTS);
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE);
        rblock = ifp->if_broot;
@@ -706,6 +710,7 @@ xfs_bmap_extents_to_btree(
        xfs_bmbt_ptr_t          *pp;            /* root block address pointer */
 
        mp = ip->i_mount;
+       ASSERT(whichfork != XFS_COW_FORK);
        ifp = XFS_IFORK_PTR(ip, whichfork);
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS);
 
@@ -748,6 +753,7 @@ xfs_bmap_extents_to_btree(
                args.type = XFS_ALLOCTYPE_START_BNO;
                args.fsbno = XFS_INO_TO_FSB(mp, ip->i_ino);
        } else if (dfops->dop_low) {
+try_another_ag:
                args.type = XFS_ALLOCTYPE_START_BNO;
                args.fsbno = *firstblock;
        } else {
@@ -762,6 +768,21 @@ xfs_bmap_extents_to_btree(
                xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
                return error;
        }
+
+       /*
+        * During a CoW operation, the allocation and bmbt updates occur in
+        * different transactions.  The mapping code tries to put new bmbt
+        * blocks near extents being mapped, but the only way to guarantee this
+        * is if the alloc and the mapping happen in a single transaction that
+        * has a block reservation.  That isn't the case here, so if we run out
+        * of space we'll try again with another AG.
+        */
+       if (xfs_sb_version_hasreflink(&cur->bc_mp->m_sb) &&
+           args.fsbno == NULLFSBLOCK &&
+           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
+               dfops->dop_low = true;
+               goto try_another_ag;
+       }
        /*
         * Allocation can't fail, the space was reserved.
         */
@@ -837,6 +858,7 @@ xfs_bmap_local_to_extents_empty(
 {
        struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, whichfork);
 
+       ASSERT(whichfork != XFS_COW_FORK);
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL);
        ASSERT(ifp->if_bytes == 0);
        ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) == 0);
@@ -896,6 +918,7 @@ xfs_bmap_local_to_extents(
         * file currently fits in an inode.
         */
        if (*firstblock == NULLFSBLOCK) {
+try_another_ag:
                args.fsbno = XFS_INO_TO_FSB(args.mp, ip->i_ino);
                args.type = XFS_ALLOCTYPE_START_BNO;
        } else {
@@ -908,6 +931,19 @@ xfs_bmap_local_to_extents(
        if (error)
                goto done;
 
+       /*
+        * During a CoW operation, the allocation and bmbt updates occur in
+        * different transactions.  The mapping code tries to put new bmbt
+        * blocks near extents being mapped, but the only way to guarantee this
+        * is if the alloc and the mapping happen in a single transaction that
+        * has a block reservation.  That isn't the case here, so if we run out
+        * of space we'll try again with another AG.
+        */
+       if (xfs_sb_version_hasreflink(&ip->i_mount->m_sb) &&
+           args.fsbno == NULLFSBLOCK &&
+           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
+               goto try_another_ag;
+       }
        /* Can't fail, the space was reserved. */
        ASSERT(args.fsbno != NULLFSBLOCK);
        ASSERT(args.len == 1);
@@ -1670,7 +1706,8 @@ xfs_bmap_one_block(
  */
 STATIC int                             /* error */
 xfs_bmap_add_extent_delay_real(
-       struct xfs_bmalloca     *bma)
+       struct xfs_bmalloca     *bma,
+       int                     whichfork)
 {
        struct xfs_bmbt_irec    *new = &bma->got;
        int                     diff;   /* temp value */
@@ -1688,11 +1725,14 @@ xfs_bmap_add_extent_delay_real(
        xfs_filblks_t           temp=0; /* value for da_new calculations */
        xfs_filblks_t           temp2=0;/* value for da_new calculations */
        int                     tmp_rval;       /* partial logging flags */
-       int                     whichfork = XFS_DATA_FORK;
        struct xfs_mount        *mp;
+       xfs_extnum_t            *nextents;
 
        mp = bma->ip->i_mount;
        ifp = XFS_IFORK_PTR(bma->ip, whichfork);
+       ASSERT(whichfork != XFS_ATTR_FORK);
+       nextents = (whichfork == XFS_COW_FORK ? &bma->ip->i_cnextents :
+                                               &bma->ip->i_d.di_nextents);
 
        ASSERT(bma->idx >= 0);
        ASSERT(bma->idx <= ifp->if_bytes / sizeof(struct xfs_bmbt_rec));
@@ -1706,6 +1746,9 @@ xfs_bmap_add_extent_delay_real(
 #define        RIGHT           r[1]
 #define        PREV            r[2]
 
+       if (whichfork == XFS_COW_FORK)
+               state |= BMAP_COWFORK;
+
        /*
         * Set up a bunch of variables to make the tests simpler.
         */
@@ -1792,7 +1835,7 @@ xfs_bmap_add_extent_delay_real(
                trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
 
                xfs_iext_remove(bma->ip, bma->idx + 1, 2, state);
-               bma->ip->i_d.di_nextents--;
+               (*nextents)--;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
@@ -1894,7 +1937,7 @@ xfs_bmap_add_extent_delay_real(
                xfs_bmbt_set_startblock(ep, new->br_startblock);
                trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
 
-               bma->ip->i_d.di_nextents++;
+               (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
@@ -1964,7 +2007,7 @@ xfs_bmap_add_extent_delay_real(
                temp = PREV.br_blockcount - new->br_blockcount;
                xfs_bmbt_set_blockcount(ep, temp);
                xfs_iext_insert(bma->ip, bma->idx, 1, new, state);
-               bma->ip->i_d.di_nextents++;
+               (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
@@ -2048,7 +2091,7 @@ xfs_bmap_add_extent_delay_real(
                trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
                xfs_bmbt_set_blockcount(ep, temp);
                xfs_iext_insert(bma->ip, bma->idx + 1, 1, new, state);
-               bma->ip->i_d.di_nextents++;
+               (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
@@ -2117,7 +2160,7 @@ xfs_bmap_add_extent_delay_real(
                RIGHT.br_blockcount = temp2;
                /* insert LEFT (r[0]) and RIGHT (r[1]) at the same time */
                xfs_iext_insert(bma->ip, bma->idx + 1, 2, &LEFT, state);
-               bma->ip->i_d.di_nextents++;
+               (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
@@ -2215,7 +2258,8 @@ xfs_bmap_add_extent_delay_real(
 
        xfs_bmap_check_leaf_extents(bma->cur, bma->ip, whichfork);
 done:
-       bma->logflags |= rval;
+       if (whichfork != XFS_COW_FORK)
+               bma->logflags |= rval;
        return error;
 #undef LEFT
 #undef RIGHT
@@ -2759,6 +2803,7 @@ done:
 STATIC void
 xfs_bmap_add_extent_hole_delay(
        xfs_inode_t             *ip,    /* incore inode pointer */
+       int                     whichfork,
        xfs_extnum_t            *idx,   /* extent number to update/insert */
        xfs_bmbt_irec_t         *new)   /* new data to add to file extents */
 {
@@ -2770,8 +2815,10 @@ xfs_bmap_add_extent_hole_delay(
        int                     state;  /* state bits, accessed thru macros */
        xfs_filblks_t           temp=0; /* temp for indirect calculations */
 
-       ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+       ifp = XFS_IFORK_PTR(ip, whichfork);
        state = 0;
+       if (whichfork == XFS_COW_FORK)
+               state |= BMAP_COWFORK;
        ASSERT(isnullstartblock(new->br_startblock));
 
        /*
@@ -2789,7 +2836,7 @@ xfs_bmap_add_extent_hole_delay(
         * Check and set flags if the current (right) segment exists.
         * If it doesn't exist, we're converting the hole at end-of-file.
         */
-       if (*idx < ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
+       if (*idx < ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
                state |= BMAP_RIGHT_VALID;
                xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx), &right);
 
@@ -2923,6 +2970,7 @@ xfs_bmap_add_extent_hole_real(
        ASSERT(!isnullstartblock(new->br_startblock));
        ASSERT(!bma->cur ||
               !(bma->cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
+       ASSERT(whichfork != XFS_COW_FORK);
 
        XFS_STATS_INC(mp, xs_add_exlist);
 
@@ -3648,7 +3696,9 @@ xfs_bmap_btalloc(
        else if (mp->m_dalign)
                stripe_align = mp->m_dalign;
 
-       if (xfs_alloc_is_userdata(ap->datatype))
+       if (ap->flags & XFS_BMAPI_COWFORK)
+               align = xfs_get_cowextsz_hint(ap->ip);
+       else if (xfs_alloc_is_userdata(ap->datatype))
                align = xfs_get_extsz_hint(ap->ip);
        if (unlikely(align)) {
                error = xfs_bmap_extsize_align(mp, &ap->got, &ap->prev,
@@ -3856,7 +3906,8 @@ xfs_bmap_btalloc(
                ASSERT(nullfb || fb_agno == args.agno ||
                       (ap->dfops->dop_low && fb_agno < args.agno));
                ap->length = args.len;
-               ap->ip->i_d.di_nblocks += args.len;
+               if (!(ap->flags & XFS_BMAPI_COWFORK))
+                       ap->ip->i_d.di_nblocks += args.len;
                xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
                if (ap->wasdel)
                        ap->ip->i_delayed_blks -= args.len;
@@ -3875,6 +3926,63 @@ xfs_bmap_btalloc(
        return 0;
 }
 
+/*
+ * For a remap operation, just "allocate" an extent at the address that the
+ * caller passed in, and ensure that the AGFL is the right size.  The caller
+ * will then map the "allocated" extent into the file somewhere.
+ */
+STATIC int
+xfs_bmap_remap_alloc(
+       struct xfs_bmalloca     *ap)
+{
+       struct xfs_trans        *tp = ap->tp;
+       struct xfs_mount        *mp = tp->t_mountp;
+       xfs_agblock_t           bno;
+       struct xfs_alloc_arg    args;
+       int                     error;
+
+       /*
+        * validate that the block number is legal - the enables us to detect
+        * and handle a silent filesystem corruption rather than crashing.
+        */
+       memset(&args, 0, sizeof(struct xfs_alloc_arg));
+       args.tp = ap->tp;
+       args.mp = ap->tp->t_mountp;
+       bno = *ap->firstblock;
+       args.agno = XFS_FSB_TO_AGNO(mp, bno);
+       args.agbno = XFS_FSB_TO_AGBNO(mp, bno);
+       if (args.agno >= mp->m_sb.sb_agcount ||
+           args.agbno >= mp->m_sb.sb_agblocks)
+               return -EFSCORRUPTED;
+
+       /* "Allocate" the extent from the range we passed in. */
+       trace_xfs_bmap_remap_alloc(ap->ip, *ap->firstblock, ap->length);
+       ap->blkno = bno;
+       ap->ip->i_d.di_nblocks += ap->length;
+       xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
+
+       /* Fix the freelist, like a real allocator does. */
+       args.datatype = ap->datatype;
+       args.pag = xfs_perag_get(args.mp, args.agno);
+       ASSERT(args.pag);
+
+       /*
+        * The freelist fixing code will decline the allocation if
+        * the size and shape of the free space doesn't allow for
+        * allocating the extent and updating all the metadata that
+        * happens during an allocation.  We're remapping, not
+        * allocating, so skip that check by pretending to be freeing.
+        */
+       error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING);
+       if (error)
+               goto error0;
+error0:
+       xfs_perag_put(args.pag);
+       if (error)
+               trace_xfs_bmap_remap_alloc_error(ap->ip, error, _RET_IP_);
+       return error;
+}
+
 /*
  * xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file.
  * It figures out where to ask the underlying allocator to put the new extent.
@@ -3883,6 +3991,8 @@ STATIC int
 xfs_bmap_alloc(
        struct xfs_bmalloca     *ap)    /* bmap alloc argument struct */
 {
+       if (ap->flags & XFS_BMAPI_REMAP)
+               return xfs_bmap_remap_alloc(ap);
        if (XFS_IS_REALTIME_INODE(ap->ip) &&
            xfs_alloc_is_userdata(ap->datatype))
                return xfs_bmap_rtalloc(ap);
@@ -4012,12 +4122,11 @@ xfs_bmapi_read(
        int                     error;
        int                     eof;
        int                     n = 0;
-       int                     whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
-                                               XFS_ATTR_FORK : XFS_DATA_FORK;
+       int                     whichfork = xfs_bmapi_whichfork(flags);
 
        ASSERT(*nmap >= 1);
        ASSERT(!(flags & ~(XFS_BMAPI_ATTRFORK|XFS_BMAPI_ENTIRE|
-                          XFS_BMAPI_IGSTATE)));
+                          XFS_BMAPI_IGSTATE|XFS_BMAPI_COWFORK)));
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_SHARED|XFS_ILOCK_EXCL));
 
        if (unlikely(XFS_TEST_ERROR(
@@ -4035,6 +4144,16 @@ xfs_bmapi_read(
 
        ifp = XFS_IFORK_PTR(ip, whichfork);
 
+       /* No CoW fork?  Return a hole. */
+       if (whichfork == XFS_COW_FORK && !ifp) {
+               mval->br_startoff = bno;
+               mval->br_startblock = HOLESTARTBLOCK;
+               mval->br_blockcount = len;
+               mval->br_state = XFS_EXT_NORM;
+               *nmap = 1;
+               return 0;
+       }
+
        if (!(ifp->if_flags & XFS_IFEXTENTS)) {
                error = xfs_iread_extents(NULL, ip, whichfork);
                if (error)
@@ -4084,6 +4203,7 @@ xfs_bmapi_read(
 int
 xfs_bmapi_reserve_delalloc(
        struct xfs_inode        *ip,
+       int                     whichfork,
        xfs_fileoff_t           aoff,
        xfs_filblks_t           len,
        struct xfs_bmbt_irec    *got,
@@ -4092,7 +4212,7 @@ xfs_bmapi_reserve_delalloc(
        int                     eof)
 {
        struct xfs_mount        *mp = ip->i_mount;
-       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, whichfork);
        xfs_extlen_t            alen;
        xfs_extlen_t            indlen;
        char                    rt = XFS_IS_REALTIME_INODE(ip);
@@ -4104,7 +4224,10 @@ xfs_bmapi_reserve_delalloc(
                alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
 
        /* Figure out the extent size, adjust alen */
-       extsz = xfs_get_extsz_hint(ip);
+       if (whichfork == XFS_COW_FORK)
+               extsz = xfs_get_cowextsz_hint(ip);
+       else
+               extsz = xfs_get_extsz_hint(ip);
        if (extsz) {
                error = xfs_bmap_extsize_align(mp, got, prev, extsz, rt, eof,
                                               1, 0, &aoff, &alen);
@@ -4151,7 +4274,7 @@ xfs_bmapi_reserve_delalloc(
        got->br_startblock = nullstartblock(indlen);
        got->br_blockcount = alen;
        got->br_state = XFS_EXT_NORM;
-       xfs_bmap_add_extent_hole_delay(ip, lastx, got);
+       xfs_bmap_add_extent_hole_delay(ip, whichfork, lastx, got);
 
        /*
         * Update our extent pointer, given that xfs_bmap_add_extent_hole_delay
@@ -4182,8 +4305,7 @@ xfs_bmapi_allocate(
        struct xfs_bmalloca     *bma)
 {
        struct xfs_mount        *mp = bma->ip->i_mount;
-       int                     whichfork = (bma->flags & XFS_BMAPI_ATTRFORK) ?
-                                               XFS_ATTR_FORK : XFS_DATA_FORK;
+       int                     whichfork = xfs_bmapi_whichfork(bma->flags);
        struct xfs_ifork        *ifp = XFS_IFORK_PTR(bma->ip, whichfork);
        int                     tmp_logflags = 0;
        int                     error;
@@ -4278,7 +4400,7 @@ xfs_bmapi_allocate(
                bma->got.br_state = XFS_EXT_UNWRITTEN;
 
        if (bma->wasdel)
-               error = xfs_bmap_add_extent_delay_real(bma);
+               error = xfs_bmap_add_extent_delay_real(bma, whichfork);
        else
                error = xfs_bmap_add_extent_hole_real(bma, whichfork);
 
@@ -4308,8 +4430,7 @@ xfs_bmapi_convert_unwritten(
        xfs_filblks_t           len,
        int                     flags)
 {
-       int                     whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
-                                               XFS_ATTR_FORK : XFS_DATA_FORK;
+       int                     whichfork = xfs_bmapi_whichfork(flags);
        struct xfs_ifork        *ifp = XFS_IFORK_PTR(bma->ip, whichfork);
        int                     tmp_logflags = 0;
        int                     error;
@@ -4325,6 +4446,8 @@ xfs_bmapi_convert_unwritten(
                        (XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT))
                return 0;
 
+       ASSERT(whichfork != XFS_COW_FORK);
+
        /*
         * Modify (by adding) the state flag, if writing.
         */
@@ -4431,8 +4554,7 @@ xfs_bmapi_write(
        orig_mval = mval;
        orig_nmap = *nmap;
 #endif
-       whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
-               XFS_ATTR_FORK : XFS_DATA_FORK;
+       whichfork = xfs_bmapi_whichfork(flags);
 
        ASSERT(*nmap >= 1);
        ASSERT(*nmap <= XFS_BMAP_MAX_NMAP);
@@ -4441,6 +4563,11 @@ xfs_bmapi_write(
        ASSERT(len > 0);
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL);
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+       ASSERT(!(flags & XFS_BMAPI_REMAP) || whichfork == XFS_DATA_FORK);
+       ASSERT(!(flags & XFS_BMAPI_PREALLOC) || !(flags & XFS_BMAPI_REMAP));
+       ASSERT(!(flags & XFS_BMAPI_CONVERT) || !(flags & XFS_BMAPI_REMAP));
+       ASSERT(!(flags & XFS_BMAPI_PREALLOC) || whichfork != XFS_COW_FORK);
+       ASSERT(!(flags & XFS_BMAPI_CONVERT) || whichfork != XFS_COW_FORK);
 
        /* zeroing is for currently only for data extents, not metadata */
        ASSERT((flags & (XFS_BMAPI_METADATA | XFS_BMAPI_ZERO)) !=
@@ -4501,6 +4628,14 @@ xfs_bmapi_write(
                inhole = eof || bma.got.br_startoff > bno;
                wasdelay = !inhole && isnullstartblock(bma.got.br_startblock);
 
+               /*
+                * Make sure we only reflink into a hole.
+                */
+               if (flags & XFS_BMAPI_REMAP)
+                       ASSERT(inhole);
+               if (flags & XFS_BMAPI_COWFORK)
+                       ASSERT(!inhole);
+
                /*
                 * First, deal with the hole before the allocated space
                 * that we found, if any.
@@ -4531,6 +4666,17 @@ xfs_bmapi_write(
                                goto error0;
                        if (bma.blkno == NULLFSBLOCK)
                                break;
+
+                       /*
+                        * If this is a CoW allocation, record the data in
+                        * the refcount btree for orphan recovery.
+                        */
+                       if (whichfork == XFS_COW_FORK) {
+                               error = xfs_refcount_alloc_cow_extent(mp, dfops,
+                                               bma.blkno, bma.length);
+                               if (error)
+                                       goto error0;
+                       }
                }
 
                /* Deal with the allocated space we found.  */
@@ -4696,7 +4842,8 @@ xfs_bmap_del_extent(
        xfs_btree_cur_t         *cur,   /* if null, not a btree */
        xfs_bmbt_irec_t         *del,   /* data to remove from extents */
        int                     *logflagsp, /* inode logging flags */
-       int                     whichfork) /* data or attr fork */
+       int                     whichfork, /* data or attr fork */
+       int                     bflags) /* bmapi flags */
 {
        xfs_filblks_t           da_new; /* new delay-alloc indirect blocks */
        xfs_filblks_t           da_old; /* old delay-alloc indirect blocks */
@@ -4725,6 +4872,8 @@ xfs_bmap_del_extent(
 
        if (whichfork == XFS_ATTR_FORK)
                state |= BMAP_ATTRFORK;
+       else if (whichfork == XFS_COW_FORK)
+               state |= BMAP_COWFORK;
 
        ifp = XFS_IFORK_PTR(ip, whichfork);
        ASSERT((*idx >= 0) && (*idx < ifp->if_bytes /
@@ -4805,6 +4954,7 @@ xfs_bmap_del_extent(
                /*
                 * Matches the whole extent.  Delete the entry.
                 */
+               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
                xfs_iext_remove(ip, *idx, 1,
                                whichfork == XFS_ATTR_FORK ? BMAP_ATTRFORK : 0);
                --*idx;
@@ -4988,9 +5138,16 @@ xfs_bmap_del_extent(
        /*
         * If we need to, add to list of extents to delete.
         */
-       if (do_fx)
-               xfs_bmap_add_free(mp, dfops, del->br_startblock,
-                               del->br_blockcount, NULL);
+       if (do_fx && !(bflags & XFS_BMAPI_REMAP)) {
+               if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK) {
+                       error = xfs_refcount_decrease_extent(mp, dfops, del);
+                       if (error)
+                               goto done;
+               } else
+                       xfs_bmap_add_free(mp, dfops, del->br_startblock,
+                                       del->br_blockcount, NULL);
+       }
+
        /*
         * Adjust inode # blocks in the file.
         */
@@ -4999,7 +5156,7 @@ xfs_bmap_del_extent(
        /*
         * Adjust quota data.
         */
-       if (qfield)
+       if (qfield && !(bflags & XFS_BMAPI_REMAP))
                xfs_trans_mod_dquot_byino(tp, ip, qfield, (long)-nblks);
 
        /*
@@ -5014,6 +5171,175 @@ done:
        return error;
 }
 
+/* Remove an extent from the CoW fork.  Similar to xfs_bmap_del_extent. */
+int
+xfs_bunmapi_cow(
+       struct xfs_inode                *ip,
+       struct xfs_bmbt_irec            *del)
+{
+       xfs_filblks_t                   da_new;
+       xfs_filblks_t                   da_old;
+       xfs_fsblock_t                   del_endblock = 0;
+       xfs_fileoff_t                   del_endoff;
+       int                             delay;
+       struct xfs_bmbt_rec_host        *ep;
+       int                             error;
+       struct xfs_bmbt_irec            got;
+       xfs_fileoff_t                   got_endoff;
+       struct xfs_ifork                *ifp;
+       struct xfs_mount                *mp;
+       xfs_filblks_t                   nblks;
+       struct xfs_bmbt_irec            new;
+       /* REFERENCED */
+       uint                            qfield;
+       xfs_filblks_t                   temp;
+       xfs_filblks_t                   temp2;
+       int                             state = BMAP_COWFORK;
+       int                             eof;
+       xfs_extnum_t                    eidx;
+
+       mp = ip->i_mount;
+       XFS_STATS_INC(mp, xs_del_exlist);
+
+       ep = xfs_bmap_search_extents(ip, del->br_startoff, XFS_COW_FORK, &eof,
+                       &eidx, &got, &new);
+
+       ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); ifp = ifp;
+       ASSERT((eidx >= 0) && (eidx < ifp->if_bytes /
+               (uint)sizeof(xfs_bmbt_rec_t)));
+       ASSERT(del->br_blockcount > 0);
+       ASSERT(got.br_startoff <= del->br_startoff);
+       del_endoff = del->br_startoff + del->br_blockcount;
+       got_endoff = got.br_startoff + got.br_blockcount;
+       ASSERT(got_endoff >= del_endoff);
+       delay = isnullstartblock(got.br_startblock);
+       ASSERT(isnullstartblock(del->br_startblock) == delay);
+       qfield = 0;
+       error = 0;
+       /*
+        * If deleting a real allocation, must free up the disk space.
+        */
+       if (!delay) {
+               nblks = del->br_blockcount;
+               qfield = XFS_TRANS_DQ_BCOUNT;
+               /*
+                * Set up del_endblock and cur for later.
+                */
+               del_endblock = del->br_startblock + del->br_blockcount;
+               da_old = da_new = 0;
+       } else {
+               da_old = startblockval(got.br_startblock);
+               da_new = 0;
+               nblks = 0;
+       }
+       qfield = qfield;
+       nblks = nblks;
+
+       /*
+        * Set flag value to use in switch statement.
+        * Left-contig is 2, right-contig is 1.
+        */
+       switch (((got.br_startoff == del->br_startoff) << 1) |
+               (got_endoff == del_endoff)) {
+       case 3:
+               /*
+                * Matches the whole extent.  Delete the entry.
+                */
+               xfs_iext_remove(ip, eidx, 1, BMAP_COWFORK);
+               --eidx;
+               break;
+
+       case 2:
+               /*
+                * Deleting the first part of the extent.
+                */
+               trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_);
+               xfs_bmbt_set_startoff(ep, del_endoff);
+               temp = got.br_blockcount - del->br_blockcount;
+               xfs_bmbt_set_blockcount(ep, temp);
+               if (delay) {
+                       temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+                               da_old);
+                       xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
+                       trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
+                       da_new = temp;
+                       break;
+               }
+               xfs_bmbt_set_startblock(ep, del_endblock);
+               trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
+               break;
+
+       case 1:
+               /*
+                * Deleting the last part of the extent.
+                */
+               temp = got.br_blockcount - del->br_blockcount;
+               trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_);
+               xfs_bmbt_set_blockcount(ep, temp);
+               if (delay) {
+                       temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+                               da_old);
+                       xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
+                       trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
+                       da_new = temp;
+                       break;
+               }
+               trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
+               break;
+
+       case 0:
+               /*
+                * Deleting the middle of the extent.
+                */
+               temp = del->br_startoff - got.br_startoff;
+               trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_);
+               xfs_bmbt_set_blockcount(ep, temp);
+               new.br_startoff = del_endoff;
+               temp2 = got_endoff - del_endoff;
+               new.br_blockcount = temp2;
+               new.br_state = got.br_state;
+               if (!delay) {
+                       new.br_startblock = del_endblock;
+               } else {
+                       temp = xfs_bmap_worst_indlen(ip, temp);
+                       xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
+                       temp2 = xfs_bmap_worst_indlen(ip, temp2);
+                       new.br_startblock = nullstartblock((int)temp2);
+                       da_new = temp + temp2;
+                       while (da_new > da_old) {
+                               if (temp) {
+                                       temp--;
+                                       da_new--;
+                                       xfs_bmbt_set_startblock(ep,
+                                               nullstartblock((int)temp));
+                               }
+                               if (da_new == da_old)
+                                       break;
+                               if (temp2) {
+                                       temp2--;
+                                       da_new--;
+                                       new.br_startblock =
+                                               nullstartblock((int)temp2);
+                               }
+                       }
+               }
+               trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
+               xfs_iext_insert(ip, eidx + 1, 1, &new, state);
+               ++eidx;
+               break;
+       }
+
+       /*
+        * Account for change in delayed indirect blocks.
+        * Nothing to do for disk quota accounting here.
+        */
+       ASSERT(da_old >= da_new);
+       if (da_old > da_new)
+               xfs_mod_fdblocks(mp, (int64_t)(da_old - da_new), false);
+
+       return error;
+}
+
 /*
  * Unmap (remove) blocks from a file.
  * If nexts is nonzero then the number of extents to remove is limited to
@@ -5021,17 +5347,16 @@ done:
  * *done is set.
  */
 int                                            /* error */
-xfs_bunmapi(
+__xfs_bunmapi(
        xfs_trans_t             *tp,            /* transaction pointer */
        struct xfs_inode        *ip,            /* incore inode */
        xfs_fileoff_t           bno,            /* starting offset to unmap */
-       xfs_filblks_t           len,            /* length to unmap in file */
+       xfs_filblks_t           *rlen,          /* i/o: amount remaining */
        int                     flags,          /* misc flags */
        xfs_extnum_t            nexts,          /* number of extents max */
        xfs_fsblock_t           *firstblock,    /* first allocated block
                                                   controls a.g. for allocs */
-       struct xfs_defer_ops    *dfops,         /* i/o: list extents to free */
-       int                     *done)          /* set if not done yet */
+       struct xfs_defer_ops    *dfops)         /* i/o: deferred updates */
 {
        xfs_btree_cur_t         *cur;           /* bmap btree cursor */
        xfs_bmbt_irec_t         del;            /* extent being deleted */
@@ -5053,11 +5378,12 @@ xfs_bunmapi(
        int                     wasdel;         /* was a delayed alloc extent */
        int                     whichfork;      /* data or attribute fork */
        xfs_fsblock_t           sum;
+       xfs_filblks_t           len = *rlen;    /* length to unmap in file */
 
        trace_xfs_bunmap(ip, bno, len, flags, _RET_IP_);
 
-       whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
-               XFS_ATTR_FORK : XFS_DATA_FORK;
+       whichfork = xfs_bmapi_whichfork(flags);
+       ASSERT(whichfork != XFS_COW_FORK);
        ifp = XFS_IFORK_PTR(ip, whichfork);
        if (unlikely(
            XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
@@ -5079,7 +5405,7 @@ xfs_bunmapi(
                return error;
        nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
        if (nextents == 0) {
-               *done = 1;
+               *rlen = 0;
                return 0;
        }
        XFS_STATS_INC(mp, xs_blk_unmap);
@@ -5324,7 +5650,7 @@ xfs_bunmapi(
                        cur->bc_private.b.flags &= ~XFS_BTCUR_BPRV_WASDEL;
 
                error = xfs_bmap_del_extent(ip, tp, &lastx, dfops, cur, &del,
-                               &tmp_logflags, whichfork);
+                               &tmp_logflags, whichfork, flags);
                logflags |= tmp_logflags;
                if (error)
                        goto error0;
@@ -5350,7 +5676,10 @@ nodelete:
                        extno++;
                }
        }
-       *done = bno == (xfs_fileoff_t)-1 || bno < start || lastx < 0;
+       if (bno == (xfs_fileoff_t)-1 || bno < start || lastx < 0)
+               *rlen = 0;
+       else
+               *rlen = bno - start + 1;
 
        /*
         * Convert to a btree if necessary.
@@ -5406,6 +5735,27 @@ error0:
        return error;
 }
 
+/* Unmap a range of a file. */
+int
+xfs_bunmapi(
+       xfs_trans_t             *tp,
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           bno,
+       xfs_filblks_t           len,
+       int                     flags,
+       xfs_extnum_t            nexts,
+       xfs_fsblock_t           *firstblock,
+       struct xfs_defer_ops    *dfops,
+       int                     *done)
+{
+       int                     error;
+
+       error = __xfs_bunmapi(tp, ip, bno, &len, flags, nexts, firstblock,
+                       dfops);
+       *done = (len == 0);
+       return error;
+}
+
 /*
  * Determine whether an extent shift can be accomplished by a merge with the
  * extent that precedes the target hole of the shift.
@@ -5985,3 +6335,146 @@ out:
        xfs_trans_cancel(tp);
        return error;
 }
+
+/* Deferred mapping is only for real extents in the data fork. */
+static bool
+xfs_bmap_is_update_needed(
+       struct xfs_bmbt_irec    *bmap)
+{
+       return  bmap->br_startblock != HOLESTARTBLOCK &&
+               bmap->br_startblock != DELAYSTARTBLOCK;
+}
+
+/* Record a bmap intent. */
+static int
+__xfs_bmap_add(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       enum xfs_bmap_intent_type       type,
+       struct xfs_inode                *ip,
+       int                             whichfork,
+       struct xfs_bmbt_irec            *bmap)
+{
+       int                             error;
+       struct xfs_bmap_intent          *bi;
+
+       trace_xfs_bmap_defer(mp,
+                       XFS_FSB_TO_AGNO(mp, bmap->br_startblock),
+                       type,
+                       XFS_FSB_TO_AGBNO(mp, bmap->br_startblock),
+                       ip->i_ino, whichfork,
+                       bmap->br_startoff,
+                       bmap->br_blockcount,
+                       bmap->br_state);
+
+       bi = kmem_alloc(sizeof(struct xfs_bmap_intent), KM_SLEEP | KM_NOFS);
+       INIT_LIST_HEAD(&bi->bi_list);
+       bi->bi_type = type;
+       bi->bi_owner = ip;
+       bi->bi_whichfork = whichfork;
+       bi->bi_bmap = *bmap;
+
+       error = xfs_defer_join(dfops, bi->bi_owner);
+       if (error) {
+               kmem_free(bi);
+               return error;
+       }
+
+       xfs_defer_add(dfops, XFS_DEFER_OPS_TYPE_BMAP, &bi->bi_list);
+       return 0;
+}
+
+/* Map an extent into a file. */
+int
+xfs_bmap_map_extent(
+       struct xfs_mount        *mp,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *PREV)
+{
+       if (!xfs_bmap_is_update_needed(PREV))
+               return 0;
+
+       return __xfs_bmap_add(mp, dfops, XFS_BMAP_MAP, ip,
+                       XFS_DATA_FORK, PREV);
+}
+
+/* Unmap an extent out of a file. */
+int
+xfs_bmap_unmap_extent(
+       struct xfs_mount        *mp,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *PREV)
+{
+       if (!xfs_bmap_is_update_needed(PREV))
+               return 0;
+
+       return __xfs_bmap_add(mp, dfops, XFS_BMAP_UNMAP, ip,
+                       XFS_DATA_FORK, PREV);
+}
+
+/*
+ * Process one of the deferred bmap operations.  We pass back the
+ * btree cursor to maintain our lock on the bmapbt between calls.
+ */
+int
+xfs_bmap_finish_one(
+       struct xfs_trans                *tp,
+       struct xfs_defer_ops            *dfops,
+       struct xfs_inode                *ip,
+       enum xfs_bmap_intent_type       type,
+       int                             whichfork,
+       xfs_fileoff_t                   startoff,
+       xfs_fsblock_t                   startblock,
+       xfs_filblks_t                   blockcount,
+       xfs_exntst_t                    state)
+{
+       struct xfs_bmbt_irec            bmap;
+       int                             nimaps = 1;
+       xfs_fsblock_t                   firstfsb;
+       int                             flags = XFS_BMAPI_REMAP;
+       int                             done;
+       int                             error = 0;
+
+       bmap.br_startblock = startblock;
+       bmap.br_startoff = startoff;
+       bmap.br_blockcount = blockcount;
+       bmap.br_state = state;
+
+       trace_xfs_bmap_deferred(tp->t_mountp,
+                       XFS_FSB_TO_AGNO(tp->t_mountp, startblock), type,
+                       XFS_FSB_TO_AGBNO(tp->t_mountp, startblock),
+                       ip->i_ino, whichfork, startoff, blockcount, state);
+
+       if (whichfork != XFS_DATA_FORK && whichfork != XFS_ATTR_FORK)
+               return -EFSCORRUPTED;
+       if (whichfork == XFS_ATTR_FORK)
+               flags |= XFS_BMAPI_ATTRFORK;
+
+       if (XFS_TEST_ERROR(false, tp->t_mountp,
+                       XFS_ERRTAG_BMAP_FINISH_ONE,
+                       XFS_RANDOM_BMAP_FINISH_ONE))
+               return -EIO;
+
+       switch (type) {
+       case XFS_BMAP_MAP:
+               firstfsb = bmap.br_startblock;
+               error = xfs_bmapi_write(tp, ip, bmap.br_startoff,
+                                       bmap.br_blockcount, flags, &firstfsb,
+                                       bmap.br_blockcount, &bmap, &nimaps,
+                                       dfops);
+               break;
+       case XFS_BMAP_UNMAP:
+               error = xfs_bunmapi(tp, ip, bmap.br_startoff,
+                               bmap.br_blockcount, flags, 1, &firstfsb,
+                               dfops, &done);
+               ASSERT(done);
+               break;
+       default:
+               ASSERT(0);
+               error = -EFSCORRUPTED;
+       }
+
+       return error;
+}
index 8395f6e8cf7ddbb1e78ab3d3d73b0c9d8324b2af..f97db7132564569dc7f2aefbf05f27da719c8e10 100644 (file)
@@ -97,6 +97,19 @@ struct xfs_extent_free_item
  */
 #define XFS_BMAPI_ZERO         0x080
 
+/*
+ * Map the inode offset to the block given in ap->firstblock.  Primarily
+ * used for reflink.  The range must be in a hole, and this flag cannot be
+ * turned on with PREALLOC or CONVERT, and cannot be used on the attr fork.
+ *
+ * For bunmapi, this flag unmaps the range without adjusting quota, reducing
+ * refcount, or freeing the blocks.
+ */
+#define XFS_BMAPI_REMAP                0x100
+
+/* Map something in the CoW fork. */
+#define XFS_BMAPI_COWFORK      0x200
+
 #define XFS_BMAPI_FLAGS \
        { XFS_BMAPI_ENTIRE,     "ENTIRE" }, \
        { XFS_BMAPI_METADATA,   "METADATA" }, \
@@ -105,12 +118,24 @@ struct xfs_extent_free_item
        { XFS_BMAPI_IGSTATE,    "IGSTATE" }, \
        { XFS_BMAPI_CONTIG,     "CONTIG" }, \
        { XFS_BMAPI_CONVERT,    "CONVERT" }, \
-       { XFS_BMAPI_ZERO,       "ZERO" }
+       { XFS_BMAPI_ZERO,       "ZERO" }, \
+       { XFS_BMAPI_REMAP,      "REMAP" }, \
+       { XFS_BMAPI_COWFORK,    "COWFORK" }
 
 
 static inline int xfs_bmapi_aflag(int w)
 {
-       return (w == XFS_ATTR_FORK ? XFS_BMAPI_ATTRFORK : 0);
+       return (w == XFS_ATTR_FORK ? XFS_BMAPI_ATTRFORK :
+              (w == XFS_COW_FORK ? XFS_BMAPI_COWFORK : 0));
+}
+
+static inline int xfs_bmapi_whichfork(int bmapi_flags)
+{
+       if (bmapi_flags & XFS_BMAPI_COWFORK)
+               return XFS_COW_FORK;
+       else if (bmapi_flags & XFS_BMAPI_ATTRFORK)
+               return XFS_ATTR_FORK;
+       return XFS_DATA_FORK;
 }
 
 /*
@@ -131,13 +156,15 @@ static inline int xfs_bmapi_aflag(int w)
 #define BMAP_LEFT_VALID                (1 << 6)
 #define BMAP_RIGHT_VALID       (1 << 7)
 #define BMAP_ATTRFORK          (1 << 8)
+#define BMAP_COWFORK           (1 << 9)
 
 #define XFS_BMAP_EXT_FLAGS \
        { BMAP_LEFT_CONTIG,     "LC" }, \
        { BMAP_RIGHT_CONTIG,    "RC" }, \
        { BMAP_LEFT_FILLING,    "LF" }, \
        { BMAP_RIGHT_FILLING,   "RF" }, \
-       { BMAP_ATTRFORK,        "ATTR" }
+       { BMAP_ATTRFORK,        "ATTR" }, \
+       { BMAP_COWFORK,         "COW" }
 
 
 /*
@@ -186,10 +213,15 @@ int       xfs_bmapi_write(struct xfs_trans *tp, struct xfs_inode *ip,
                xfs_fsblock_t *firstblock, xfs_extlen_t total,
                struct xfs_bmbt_irec *mval, int *nmap,
                struct xfs_defer_ops *dfops);
+int    __xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
+               xfs_fileoff_t bno, xfs_filblks_t *rlen, int flags,
+               xfs_extnum_t nexts, xfs_fsblock_t *firstblock,
+               struct xfs_defer_ops *dfops);
 int    xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
                xfs_fileoff_t bno, xfs_filblks_t len, int flags,
                xfs_extnum_t nexts, xfs_fsblock_t *firstblock,
                struct xfs_defer_ops *dfops, int *done);
+int    xfs_bunmapi_cow(struct xfs_inode *ip, struct xfs_bmbt_irec *del);
 int    xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx,
                xfs_extnum_t num);
 uint   xfs_default_attroffset(struct xfs_inode *ip);
@@ -203,8 +235,31 @@ struct xfs_bmbt_rec_host *
        xfs_bmap_search_extents(struct xfs_inode *ip, xfs_fileoff_t bno,
                int fork, int *eofp, xfs_extnum_t *lastxp,
                struct xfs_bmbt_irec *gotp, struct xfs_bmbt_irec *prevp);
-int    xfs_bmapi_reserve_delalloc(struct xfs_inode *ip, xfs_fileoff_t aoff,
-               xfs_filblks_t len, struct xfs_bmbt_irec *got,
-               struct xfs_bmbt_irec *prev, xfs_extnum_t *lastx, int eof);
+int    xfs_bmapi_reserve_delalloc(struct xfs_inode *ip, int whichfork,
+               xfs_fileoff_t aoff, xfs_filblks_t len,
+               struct xfs_bmbt_irec *got, struct xfs_bmbt_irec *prev,
+               xfs_extnum_t *lastx, int eof);
+
+enum xfs_bmap_intent_type {
+       XFS_BMAP_MAP = 1,
+       XFS_BMAP_UNMAP,
+};
+
+struct xfs_bmap_intent {
+       struct list_head                        bi_list;
+       enum xfs_bmap_intent_type               bi_type;
+       struct xfs_inode                        *bi_owner;
+       int                                     bi_whichfork;
+       struct xfs_bmbt_irec                    bi_bmap;
+};
+
+int    xfs_bmap_finish_one(struct xfs_trans *tp, struct xfs_defer_ops *dfops,
+               struct xfs_inode *ip, enum xfs_bmap_intent_type type,
+               int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
+               xfs_filblks_t blockcount, xfs_exntst_t state);
+int    xfs_bmap_map_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
+               struct xfs_inode *ip, struct xfs_bmbt_irec *imap);
+int    xfs_bmap_unmap_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
+               struct xfs_inode *ip, struct xfs_bmbt_irec *imap);
 
 #endif /* __XFS_BMAP_H__ */
index cd85274e810cd1457dd62dfa7abfb725138a35fc..8007d2ba9aef9c1c40e6f7159fc2a75a9d142213 100644 (file)
@@ -453,6 +453,7 @@ xfs_bmbt_alloc_block(
 
        if (args.fsbno == NULLFSBLOCK) {
                args.fsbno = be64_to_cpu(start->l);
+try_another_ag:
                args.type = XFS_ALLOCTYPE_START_BNO;
                /*
                 * Make sure there is sufficient room left in the AG to
@@ -482,6 +483,22 @@ xfs_bmbt_alloc_block(
        if (error)
                goto error0;
 
+       /*
+        * During a CoW operation, the allocation and bmbt updates occur in
+        * different transactions.  The mapping code tries to put new bmbt
+        * blocks near extents being mapped, but the only way to guarantee this
+        * is if the alloc and the mapping happen in a single transaction that
+        * has a block reservation.  That isn't the case here, so if we run out
+        * of space we'll try again with another AG.
+        */
+       if (xfs_sb_version_hasreflink(&cur->bc_mp->m_sb) &&
+           args.fsbno == NULLFSBLOCK &&
+           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
+               cur->bc_private.b.dfops->dop_low = true;
+               args.fsbno = cur->bc_private.b.firstblock;
+               goto try_another_ag;
+       }
+
        if (args.fsbno == NULLFSBLOCK && args.minleft) {
                /*
                 * Could not find an AG with enough free space to satisfy
@@ -777,6 +794,7 @@ xfs_bmbt_init_cursor(
 {
        struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, whichfork);
        struct xfs_btree_cur    *cur;
+       ASSERT(whichfork != XFS_COW_FORK);
 
        cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
 
index aa1752f918b8d564e3eba5671262dc7c42e92604..5c8e6f2ce44f461d343a98b6b49ad6b0b09a3b8b 100644 (file)
@@ -45,9 +45,10 @@ kmem_zone_t  *xfs_btree_cur_zone;
  */
 static const __uint32_t xfs_magics[2][XFS_BTNUM_MAX] = {
        { XFS_ABTB_MAGIC, XFS_ABTC_MAGIC, 0, XFS_BMAP_MAGIC, XFS_IBT_MAGIC,
-         XFS_FIBT_MAGIC },
+         XFS_FIBT_MAGIC, 0 },
        { XFS_ABTB_CRC_MAGIC, XFS_ABTC_CRC_MAGIC, XFS_RMAP_CRC_MAGIC,
-         XFS_BMAP_CRC_MAGIC, XFS_IBT_CRC_MAGIC, XFS_FIBT_CRC_MAGIC }
+         XFS_BMAP_CRC_MAGIC, XFS_IBT_CRC_MAGIC, XFS_FIBT_CRC_MAGIC,
+         XFS_REFC_CRC_MAGIC }
 };
 #define xfs_btree_magic(cur) \
        xfs_magics[!!((cur)->bc_flags & XFS_BTREE_CRC_BLOCKS)][cur->bc_btnum]
@@ -1216,6 +1217,9 @@ xfs_btree_set_refs(
        case XFS_BTNUM_RMAP:
                xfs_buf_set_ref(bp, XFS_RMAP_BTREE_REF);
                break;
+       case XFS_BTNUM_REFC:
+               xfs_buf_set_ref(bp, XFS_REFC_BTREE_REF);
+               break;
        default:
                ASSERT(0);
        }
index 3f8556a5c2ad789c04c884505472c47cd7d69f0e..c2b01d1c79ee3ea5c9bca058359a9b73f0f70bd5 100644 (file)
@@ -49,6 +49,7 @@ union xfs_btree_key {
        struct xfs_inobt_key            inobt;
        struct xfs_rmap_key             rmap;
        struct xfs_rmap_key             __rmap_bigkey[2];
+       struct xfs_refcount_key         refc;
 };
 
 union xfs_btree_rec {
@@ -57,6 +58,7 @@ union xfs_btree_rec {
        struct xfs_alloc_rec            alloc;
        struct xfs_inobt_rec            inobt;
        struct xfs_rmap_rec             rmap;
+       struct xfs_refcount_rec         refc;
 };
 
 /*
@@ -72,6 +74,7 @@ union xfs_btree_rec {
 #define        XFS_BTNUM_INO   ((xfs_btnum_t)XFS_BTNUM_INOi)
 #define        XFS_BTNUM_FINO  ((xfs_btnum_t)XFS_BTNUM_FINOi)
 #define        XFS_BTNUM_RMAP  ((xfs_btnum_t)XFS_BTNUM_RMAPi)
+#define        XFS_BTNUM_REFC  ((xfs_btnum_t)XFS_BTNUM_REFCi)
 
 /*
  * For logging record fields.
@@ -105,6 +108,7 @@ do {    \
        case XFS_BTNUM_INO: __XFS_BTREE_STATS_INC(__mp, ibt, stat); break; \
        case XFS_BTNUM_FINO: __XFS_BTREE_STATS_INC(__mp, fibt, stat); break; \
        case XFS_BTNUM_RMAP: __XFS_BTREE_STATS_INC(__mp, rmap, stat); break; \
+       case XFS_BTNUM_REFC: __XFS_BTREE_STATS_INC(__mp, refcbt, stat); break; \
        case XFS_BTNUM_MAX: ASSERT(0); /* fucking gcc */ ; break;       \
        }       \
 } while (0)
@@ -127,6 +131,8 @@ do {    \
                __XFS_BTREE_STATS_ADD(__mp, fibt, stat, val); break; \
        case XFS_BTNUM_RMAP:    \
                __XFS_BTREE_STATS_ADD(__mp, rmap, stat, val); break; \
+       case XFS_BTNUM_REFC:    \
+               __XFS_BTREE_STATS_ADD(__mp, refcbt, stat, val); break; \
        case XFS_BTNUM_MAX: ASSERT(0); /* fucking gcc */ ; break; \
        }       \
 } while (0)
@@ -217,6 +223,15 @@ union xfs_btree_irec {
        struct xfs_bmbt_irec            b;
        struct xfs_inobt_rec_incore     i;
        struct xfs_rmap_irec            r;
+       struct xfs_refcount_irec        rc;
+};
+
+/* Per-AG btree private information. */
+union xfs_btree_cur_private {
+       struct {
+               unsigned long   nr_ops;         /* # record updates */
+               int             shape_changes;  /* # of extent splits */
+       } refc;
 };
 
 /*
@@ -243,6 +258,7 @@ typedef struct xfs_btree_cur
                        struct xfs_buf  *agbp;  /* agf/agi buffer pointer */
                        struct xfs_defer_ops *dfops;    /* deferred updates */
                        xfs_agnumber_t  agno;   /* ag number */
+                       union xfs_btree_cur_private     priv;
                } a;
                struct {                        /* needed for BMAP */
                        struct xfs_inode *ip;   /* pointer to our inode */
index e96533d178cfceb6afead75e874cb69909883dbb..f6e93ef0bffe8a4620a136b1e7f523bb1343a941 100644 (file)
@@ -51,6 +51,8 @@ struct xfs_defer_pending {
  * find all the space it needs.
  */
 enum xfs_defer_ops_type {
+       XFS_DEFER_OPS_TYPE_BMAP,
+       XFS_DEFER_OPS_TYPE_REFCOUNT,
        XFS_DEFER_OPS_TYPE_RMAP,
        XFS_DEFER_OPS_TYPE_FREE,
        XFS_DEFER_OPS_TYPE_MAX,
index 270fb5cf4fa11eb1ed1ce2170a35b6af1754e460..f6547fc5e016e75a130a738c3e4a1b3a087d7281 100644 (file)
@@ -456,9 +456,11 @@ xfs_sb_has_compat_feature(
 
 #define XFS_SB_FEAT_RO_COMPAT_FINOBT   (1 << 0)                /* free inode btree */
 #define XFS_SB_FEAT_RO_COMPAT_RMAPBT   (1 << 1)                /* reverse map btree */
+#define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)                /* reflinked files */
 #define XFS_SB_FEAT_RO_COMPAT_ALL \
                (XFS_SB_FEAT_RO_COMPAT_FINOBT | \
-                XFS_SB_FEAT_RO_COMPAT_RMAPBT)
+                XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
+                XFS_SB_FEAT_RO_COMPAT_REFLINK)
 #define XFS_SB_FEAT_RO_COMPAT_UNKNOWN  ~XFS_SB_FEAT_RO_COMPAT_ALL
 static inline bool
 xfs_sb_has_ro_compat_feature(
@@ -546,6 +548,12 @@ static inline bool xfs_sb_version_hasrmapbt(struct xfs_sb *sbp)
                (sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_RMAPBT);
 }
 
+static inline bool xfs_sb_version_hasreflink(struct xfs_sb *sbp)
+{
+       return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5 &&
+               (sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_REFLINK);
+}
+
 /*
  * end of superblock version macros
  */
@@ -641,14 +649,17 @@ typedef struct xfs_agf {
        uuid_t          agf_uuid;       /* uuid of filesystem */
 
        __be32          agf_rmap_blocks;        /* rmapbt blocks used */
-       __be32          agf_padding;            /* padding */
+       __be32          agf_refcount_blocks;    /* refcountbt blocks used */
+
+       __be32          agf_refcount_root;      /* refcount tree root block */
+       __be32          agf_refcount_level;     /* refcount btree levels */
 
        /*
         * reserve some contiguous space for future logged fields before we add
         * the unlogged fields. This makes the range logging via flags and
         * structure offsets much simpler.
         */
-       __be64          agf_spare64[15];
+       __be64          agf_spare64[14];
 
        /* unlogged fields, written during buffer writeback. */
        __be64          agf_lsn;        /* last write sequence */
@@ -674,8 +685,11 @@ typedef struct xfs_agf {
 #define        XFS_AGF_BTREEBLKS       0x00000800
 #define        XFS_AGF_UUID            0x00001000
 #define        XFS_AGF_RMAP_BLOCKS     0x00002000
-#define        XFS_AGF_SPARE64         0x00004000
-#define        XFS_AGF_NUM_BITS        15
+#define        XFS_AGF_REFCOUNT_BLOCKS 0x00004000
+#define        XFS_AGF_REFCOUNT_ROOT   0x00008000
+#define        XFS_AGF_REFCOUNT_LEVEL  0x00010000
+#define        XFS_AGF_SPARE64         0x00020000
+#define        XFS_AGF_NUM_BITS        18
 #define        XFS_AGF_ALL_BITS        ((1 << XFS_AGF_NUM_BITS) - 1)
 
 #define XFS_AGF_FLAGS \
@@ -693,6 +707,9 @@ typedef struct xfs_agf {
        { XFS_AGF_BTREEBLKS,    "BTREEBLKS" }, \
        { XFS_AGF_UUID,         "UUID" }, \
        { XFS_AGF_RMAP_BLOCKS,  "RMAP_BLOCKS" }, \
+       { XFS_AGF_REFCOUNT_BLOCKS,      "REFCOUNT_BLOCKS" }, \
+       { XFS_AGF_REFCOUNT_ROOT,        "REFCOUNT_ROOT" }, \
+       { XFS_AGF_REFCOUNT_LEVEL,       "REFCOUNT_LEVEL" }, \
        { XFS_AGF_SPARE64,      "SPARE64" }
 
 /* disk block (xfs_daddr_t) in the AG */
@@ -885,7 +902,8 @@ typedef struct xfs_dinode {
        __be64          di_changecount; /* number of attribute changes */
        __be64          di_lsn;         /* flush sequence */
        __be64          di_flags2;      /* more random flags */
-       __u8            di_pad2[16];    /* more padding for future expansion */
+       __be32          di_cowextsize;  /* basic cow extent size for file */
+       __u8            di_pad2[12];    /* more padding for future expansion */
 
        /* fields only written to during inode creation */
        xfs_timestamp_t di_crtime;      /* time created */
@@ -1041,9 +1059,14 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev)
  * 16 bits of the XFS_XFLAG_s range.
  */
 #define XFS_DIFLAG2_DAX_BIT    0       /* use DAX for this inode */
+#define XFS_DIFLAG2_REFLINK_BIT        1       /* file's blocks may be shared */
+#define XFS_DIFLAG2_COWEXTSIZE_BIT   2  /* copy on write extent size hint */
 #define XFS_DIFLAG2_DAX                (1 << XFS_DIFLAG2_DAX_BIT)
+#define XFS_DIFLAG2_REFLINK     (1 << XFS_DIFLAG2_REFLINK_BIT)
+#define XFS_DIFLAG2_COWEXTSIZE  (1 << XFS_DIFLAG2_COWEXTSIZE_BIT)
 
-#define XFS_DIFLAG2_ANY                (XFS_DIFLAG2_DAX)
+#define XFS_DIFLAG2_ANY \
+       (XFS_DIFLAG2_DAX | XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE)
 
 /*
  * Inode number format:
@@ -1353,7 +1376,9 @@ struct xfs_owner_info {
 #define XFS_RMAP_OWN_AG                (-5ULL) /* AG freespace btree blocks */
 #define XFS_RMAP_OWN_INOBT     (-6ULL) /* Inode btree blocks */
 #define XFS_RMAP_OWN_INODES    (-7ULL) /* Inode chunk */
-#define XFS_RMAP_OWN_MIN       (-8ULL) /* guard */
+#define XFS_RMAP_OWN_REFC      (-8ULL) /* refcount tree */
+#define XFS_RMAP_OWN_COW       (-9ULL) /* cow allocations */
+#define XFS_RMAP_OWN_MIN       (-10ULL) /* guard */
 
 #define XFS_RMAP_NON_INODE_OWNER(owner)        (!!((owner) & (1ULL << 63)))
 
@@ -1433,6 +1458,62 @@ typedef __be32 xfs_rmap_ptr_t;
         XFS_FIBT_BLOCK(mp) + 1 : \
         XFS_IBT_BLOCK(mp) + 1)
 
+/*
+ * Reference Count Btree format definitions
+ *
+ */
+#define        XFS_REFC_CRC_MAGIC      0x52334643      /* 'R3FC' */
+
+unsigned int xfs_refc_block(struct xfs_mount *mp);
+
+/*
+ * Data record/key structure
+ *
+ * Each record associates a range of physical blocks (starting at
+ * rc_startblock and ending rc_blockcount blocks later) with a reference
+ * count (rc_refcount).  Extents that are being used to stage a copy on
+ * write (CoW) operation are recorded in the refcount btree with a
+ * refcount of 1.  All other records must have a refcount > 1 and must
+ * track an extent mapped only by file data forks.
+ *
+ * Extents with a single owner (attributes, metadata, non-shared file
+ * data) are not tracked here.  Free space is also not tracked here.
+ * This is consistent with pre-reflink XFS.
+ */
+
+/*
+ * Extents that are being used to stage a copy on write are stored
+ * in the refcount btree with a refcount of 1 and the upper bit set
+ * on the startblock.  This speeds up mount time deletion of stale
+ * staging extents because they're all at the right side of the tree.
+ */
+#define XFS_REFC_COW_START             ((xfs_agblock_t)(1U << 31))
+#define REFCNTBT_COWFLAG_BITLEN                1
+#define REFCNTBT_AGBLOCK_BITLEN                31
+
+struct xfs_refcount_rec {
+       __be32          rc_startblock;  /* starting block number */
+       __be32          rc_blockcount;  /* count of blocks */
+       __be32          rc_refcount;    /* number of inodes linked here */
+};
+
+struct xfs_refcount_key {
+       __be32          rc_startblock;  /* starting block number */
+};
+
+struct xfs_refcount_irec {
+       xfs_agblock_t   rc_startblock;  /* starting block number */
+       xfs_extlen_t    rc_blockcount;  /* count of free blocks */
+       xfs_nlink_t     rc_refcount;    /* number of inodes linked here */
+};
+
+#define MAXREFCOUNT    ((xfs_nlink_t)~0U)
+#define MAXREFCEXTLEN  ((xfs_extlen_t)~0U)
+
+/* btree pointer type */
+typedef __be32 xfs_refcount_ptr_t;
+
+
 /*
  * BMAP Btree format definitions
  *
index 79455058b752588e7855afde61ad59ba4ce3186f..b72dc821d78b97ba5f93a21122eab9946e2e947a 100644 (file)
@@ -81,14 +81,16 @@ struct getbmapx {
 #define BMV_IF_PREALLOC                0x4     /* rtn status BMV_OF_PREALLOC if req */
 #define BMV_IF_DELALLOC                0x8     /* rtn status BMV_OF_DELALLOC if req */
 #define BMV_IF_NO_HOLES                0x10    /* Do not return holes */
+#define BMV_IF_COWFORK         0x20    /* return CoW fork rather than data */
 #define BMV_IF_VALID   \
        (BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC|  \
-        BMV_IF_DELALLOC|BMV_IF_NO_HOLES)
+        BMV_IF_DELALLOC|BMV_IF_NO_HOLES|BMV_IF_COWFORK)
 
 /*     bmv_oflags values - returned for each non-header segment */
 #define BMV_OF_PREALLOC                0x1     /* segment = unwritten pre-allocation */
 #define BMV_OF_DELALLOC                0x2     /* segment = delayed allocation */
 #define BMV_OF_LAST            0x4     /* segment is the last in the file */
+#define BMV_OF_SHARED          0x8     /* segment shared with another file */
 
 /*
  * Structure for XFS_IOC_FSSETDM.
@@ -206,7 +208,8 @@ typedef struct xfs_fsop_resblks {
 #define XFS_FSOP_GEOM_FLAGS_FTYPE      0x10000 /* inode directory types */
 #define XFS_FSOP_GEOM_FLAGS_FINOBT     0x20000 /* free inode btree */
 #define XFS_FSOP_GEOM_FLAGS_SPINODES   0x40000 /* sparse inode chunks  */
-#define XFS_FSOP_GEOM_FLAGS_RMAPBT     0x80000 /* Reverse mapping btree */
+#define XFS_FSOP_GEOM_FLAGS_RMAPBT     0x80000 /* reverse mapping btree */
+#define XFS_FSOP_GEOM_FLAGS_REFLINK    0x100000 /* files can share blocks */
 
 /*
  * Minimum and maximum sizes need for growth checks.
@@ -275,7 +278,8 @@ typedef struct xfs_bstat {
 #define        bs_projid       bs_projid_lo    /* (previously just bs_projid)  */
        __u16           bs_forkoff;     /* inode fork offset in bytes   */
        __u16           bs_projid_hi;   /* higher part of project id    */
-       unsigned char   bs_pad[10];     /* pad space, unused            */
+       unsigned char   bs_pad[6];      /* pad space, unused            */
+       __u32           bs_cowextsize;  /* cow extent size              */
        __u32           bs_dmevmask;    /* DMIG event mask              */
        __u16           bs_dmstate;     /* DMIG state info              */
        __u16           bs_aextents;    /* attribute number of extents  */
index 4b9769e23c834278eabe70ea429dacabd2d934f2..8de9a3a29589bd59c0684c8eab278db3edceba53 100644 (file)
@@ -256,6 +256,7 @@ xfs_inode_from_disk(
                to->di_crtime.t_sec = be32_to_cpu(from->di_crtime.t_sec);
                to->di_crtime.t_nsec = be32_to_cpu(from->di_crtime.t_nsec);
                to->di_flags2 = be64_to_cpu(from->di_flags2);
+               to->di_cowextsize = be32_to_cpu(from->di_cowextsize);
        }
 }
 
@@ -305,7 +306,7 @@ xfs_inode_to_disk(
                to->di_crtime.t_sec = cpu_to_be32(from->di_crtime.t_sec);
                to->di_crtime.t_nsec = cpu_to_be32(from->di_crtime.t_nsec);
                to->di_flags2 = cpu_to_be64(from->di_flags2);
-
+               to->di_cowextsize = cpu_to_be32(from->di_cowextsize);
                to->di_ino = cpu_to_be64(ip->i_ino);
                to->di_lsn = cpu_to_be64(lsn);
                memset(to->di_pad2, 0, sizeof(to->di_pad2));
@@ -357,6 +358,7 @@ xfs_log_dinode_to_disk(
                to->di_crtime.t_sec = cpu_to_be32(from->di_crtime.t_sec);
                to->di_crtime.t_nsec = cpu_to_be32(from->di_crtime.t_nsec);
                to->di_flags2 = cpu_to_be64(from->di_flags2);
+               to->di_cowextsize = cpu_to_be32(from->di_cowextsize);
                to->di_ino = cpu_to_be64(from->di_ino);
                to->di_lsn = cpu_to_be64(from->di_lsn);
                memcpy(to->di_pad2, from->di_pad2, sizeof(to->di_pad2));
@@ -373,6 +375,9 @@ xfs_dinode_verify(
        struct xfs_inode        *ip,
        struct xfs_dinode       *dip)
 {
+       uint16_t                flags;
+       uint64_t                flags2;
+
        if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC))
                return false;
 
@@ -389,6 +394,23 @@ xfs_dinode_verify(
                return false;
        if (!uuid_equal(&dip->di_uuid, &mp->m_sb.sb_meta_uuid))
                return false;
+
+       flags = be16_to_cpu(dip->di_flags);
+       flags2 = be64_to_cpu(dip->di_flags2);
+
+       /* don't allow reflink/cowextsize if we don't have reflink */
+       if ((flags2 & (XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE)) &&
+            !xfs_sb_version_hasreflink(&mp->m_sb))
+               return false;
+
+       /* don't let reflink and realtime mix */
+       if ((flags2 & XFS_DIFLAG2_REFLINK) && (flags & XFS_DIFLAG_REALTIME))
+               return false;
+
+       /* don't let reflink and dax mix */
+       if ((flags2 & XFS_DIFLAG2_REFLINK) && (flags2 & XFS_DIFLAG2_DAX))
+               return false;
+
        return true;
 }
 
index 7c4dd321b2152915c2d9075222b4d757a08539cf..62d9d4681c8c28a1294b575903f0720693bc6666 100644 (file)
@@ -47,6 +47,7 @@ struct xfs_icdinode {
        __uint16_t      di_flags;       /* random flags, XFS_DIFLAG_... */
 
        __uint64_t      di_flags2;      /* more random flags */
+       __uint32_t      di_cowextsize;  /* basic cow extent size for file */
 
        xfs_ictimestamp_t di_crtime;    /* time created */
 };
index bbcc8c7a44b3ffde66bf152583e87ebbd9763da7..5dd56d3dbb3aeb41e7e46b921ca9052dd9066c41 100644 (file)
@@ -121,6 +121,26 @@ xfs_iformat_fork(
                return -EFSCORRUPTED;
        }
 
+       if (unlikely(xfs_is_reflink_inode(ip) &&
+           (VFS_I(ip)->i_mode & S_IFMT) != S_IFREG)) {
+               xfs_warn(ip->i_mount,
+                       "corrupt dinode %llu, wrong file type for reflink.",
+                       ip->i_ino);
+               XFS_CORRUPTION_ERROR("xfs_iformat(reflink)",
+                                    XFS_ERRLEVEL_LOW, ip->i_mount, dip);
+               return -EFSCORRUPTED;
+       }
+
+       if (unlikely(xfs_is_reflink_inode(ip) &&
+           (ip->i_d.di_flags & XFS_DIFLAG_REALTIME))) {
+               xfs_warn(ip->i_mount,
+                       "corrupt dinode %llu, has reflink+realtime flag set.",
+                       ip->i_ino);
+               XFS_CORRUPTION_ERROR("xfs_iformat(reflink)",
+                                    XFS_ERRLEVEL_LOW, ip->i_mount, dip);
+               return -EFSCORRUPTED;
+       }
+
        switch (VFS_I(ip)->i_mode & S_IFMT) {
        case S_IFIFO:
        case S_IFCHR:
@@ -186,9 +206,14 @@ xfs_iformat_fork(
                XFS_ERROR_REPORT("xfs_iformat(7)", XFS_ERRLEVEL_LOW, ip->i_mount);
                return -EFSCORRUPTED;
        }
-       if (error) {
+       if (error)
                return error;
+
+       if (xfs_is_reflink_inode(ip)) {
+               ASSERT(ip->i_cowfp == NULL);
+               xfs_ifork_init_cow(ip);
        }
+
        if (!XFS_DFORK_Q(dip))
                return 0;
 
@@ -208,7 +233,8 @@ xfs_iformat_fork(
                        XFS_CORRUPTION_ERROR("xfs_iformat(8)",
                                             XFS_ERRLEVEL_LOW,
                                             ip->i_mount, dip);
-                       return -EFSCORRUPTED;
+                       error = -EFSCORRUPTED;
+                       break;
                }
 
                error = xfs_iformat_local(ip, dip, XFS_ATTR_FORK, size);
@@ -226,6 +252,9 @@ xfs_iformat_fork(
        if (error) {
                kmem_zone_free(xfs_ifork_zone, ip->i_afp);
                ip->i_afp = NULL;
+               if (ip->i_cowfp)
+                       kmem_zone_free(xfs_ifork_zone, ip->i_cowfp);
+               ip->i_cowfp = NULL;
                xfs_idestroy_fork(ip, XFS_DATA_FORK);
        }
        return error;
@@ -740,6 +769,9 @@ xfs_idestroy_fork(
        if (whichfork == XFS_ATTR_FORK) {
                kmem_zone_free(xfs_ifork_zone, ip->i_afp);
                ip->i_afp = NULL;
+       } else if (whichfork == XFS_COW_FORK) {
+               kmem_zone_free(xfs_ifork_zone, ip->i_cowfp);
+               ip->i_cowfp = NULL;
        }
 }
 
@@ -927,6 +959,19 @@ xfs_iext_get_ext(
        }
 }
 
+/* Convert bmap state flags to an inode fork. */
+struct xfs_ifork *
+xfs_iext_state_to_fork(
+       struct xfs_inode        *ip,
+       int                     state)
+{
+       if (state & BMAP_COWFORK)
+               return ip->i_cowfp;
+       else if (state & BMAP_ATTRFORK)
+               return ip->i_afp;
+       return &ip->i_df;
+}
+
 /*
  * Insert new item(s) into the extent records for incore inode
  * fork 'ifp'.  'count' new items are inserted at index 'idx'.
@@ -939,7 +984,7 @@ xfs_iext_insert(
        xfs_bmbt_irec_t *new,           /* items to insert */
        int             state)          /* type of extent conversion */
 {
-       xfs_ifork_t     *ifp = (state & BMAP_ATTRFORK) ? ip->i_afp : &ip->i_df;
+       xfs_ifork_t     *ifp = xfs_iext_state_to_fork(ip, state);
        xfs_extnum_t    i;              /* extent record index */
 
        trace_xfs_iext_insert(ip, idx, new, state, _RET_IP_);
@@ -1189,7 +1234,7 @@ xfs_iext_remove(
        int             ext_diff,       /* number of extents to remove */
        int             state)          /* type of extent conversion */
 {
-       xfs_ifork_t     *ifp = (state & BMAP_ATTRFORK) ? ip->i_afp : &ip->i_df;
+       xfs_ifork_t     *ifp = xfs_iext_state_to_fork(ip, state);
        xfs_extnum_t    nextents;       /* number of extents in file */
        int             new_size;       /* size of extents after removal */
 
@@ -1934,3 +1979,20 @@ xfs_iext_irec_update_extoffs(
                ifp->if_u1.if_ext_irec[i].er_extoff += ext_diff;
        }
 }
+
+/*
+ * Initialize an inode's copy-on-write fork.
+ */
+void
+xfs_ifork_init_cow(
+       struct xfs_inode        *ip)
+{
+       if (ip->i_cowfp)
+               return;
+
+       ip->i_cowfp = kmem_zone_zalloc(xfs_ifork_zone,
+                                      KM_SLEEP | KM_NOFS);
+       ip->i_cowfp->if_flags = XFS_IFEXTENTS;
+       ip->i_cformat = XFS_DINODE_FMT_EXTENTS;
+       ip->i_cnextents = 0;
+}
index f95e072ae6468240a6ae0cb8d1dc094eafbf59fc..c9476f50e32d116109d1f71dad3895c5659496b8 100644 (file)
@@ -92,7 +92,9 @@ typedef struct xfs_ifork {
 #define XFS_IFORK_PTR(ip,w)            \
        ((w) == XFS_DATA_FORK ? \
                &(ip)->i_df : \
-               (ip)->i_afp)
+               ((w) == XFS_ATTR_FORK ? \
+                       (ip)->i_afp : \
+                       (ip)->i_cowfp))
 #define XFS_IFORK_DSIZE(ip) \
        (XFS_IFORK_Q(ip) ? \
                XFS_IFORK_BOFF(ip) : \
@@ -105,26 +107,38 @@ typedef struct xfs_ifork {
 #define XFS_IFORK_SIZE(ip,w) \
        ((w) == XFS_DATA_FORK ? \
                XFS_IFORK_DSIZE(ip) : \
-               XFS_IFORK_ASIZE(ip))
+               ((w) == XFS_ATTR_FORK ? \
+                       XFS_IFORK_ASIZE(ip) : \
+                       0))
 #define XFS_IFORK_FORMAT(ip,w) \
        ((w) == XFS_DATA_FORK ? \
                (ip)->i_d.di_format : \
-               (ip)->i_d.di_aformat)
+               ((w) == XFS_ATTR_FORK ? \
+                       (ip)->i_d.di_aformat : \
+                       (ip)->i_cformat))
 #define XFS_IFORK_FMT_SET(ip,w,n) \
        ((w) == XFS_DATA_FORK ? \
                ((ip)->i_d.di_format = (n)) : \
-               ((ip)->i_d.di_aformat = (n)))
+               ((w) == XFS_ATTR_FORK ? \
+                       ((ip)->i_d.di_aformat = (n)) : \
+                       ((ip)->i_cformat = (n))))
 #define XFS_IFORK_NEXTENTS(ip,w) \
        ((w) == XFS_DATA_FORK ? \
                (ip)->i_d.di_nextents : \
-               (ip)->i_d.di_anextents)
+               ((w) == XFS_ATTR_FORK ? \
+                       (ip)->i_d.di_anextents : \
+                       (ip)->i_cnextents))
 #define XFS_IFORK_NEXT_SET(ip,w,n) \
        ((w) == XFS_DATA_FORK ? \
                ((ip)->i_d.di_nextents = (n)) : \
-               ((ip)->i_d.di_anextents = (n)))
+               ((w) == XFS_ATTR_FORK ? \
+                       ((ip)->i_d.di_anextents = (n)) : \
+                       ((ip)->i_cnextents = (n))))
 #define XFS_IFORK_MAXEXT(ip, w) \
        (XFS_IFORK_SIZE(ip, w) / sizeof(xfs_bmbt_rec_t))
 
+struct xfs_ifork *xfs_iext_state_to_fork(struct xfs_inode *ip, int state);
+
 int            xfs_iformat_fork(struct xfs_inode *, struct xfs_dinode *);
 void           xfs_iflush_fork(struct xfs_inode *, struct xfs_dinode *,
                                struct xfs_inode_log_item *, int);
@@ -169,4 +183,6 @@ void                xfs_iext_irec_update_extoffs(struct xfs_ifork *, int, int);
 
 extern struct kmem_zone        *xfs_ifork_zone;
 
+extern void xfs_ifork_init_cow(struct xfs_inode *ip);
+
 #endif /* __XFS_INODE_FORK_H__ */
index fc5eef85d61ea7d865a571482f44cfac2695e81a..083cdd6d6c28cecdf9af7d45df56cdc6d9a13665 100644 (file)
@@ -112,7 +112,11 @@ static inline uint xlog_get_cycle(char *ptr)
 #define XLOG_REG_TYPE_ICREATE          20
 #define XLOG_REG_TYPE_RUI_FORMAT       21
 #define XLOG_REG_TYPE_RUD_FORMAT       22
-#define XLOG_REG_TYPE_MAX              22
+#define XLOG_REG_TYPE_CUI_FORMAT       23
+#define XLOG_REG_TYPE_CUD_FORMAT       24
+#define XLOG_REG_TYPE_BUI_FORMAT       25
+#define XLOG_REG_TYPE_BUD_FORMAT       26
+#define XLOG_REG_TYPE_MAX              26
 
 /*
  * Flags to log operation header
@@ -231,6 +235,10 @@ typedef struct xfs_trans_header {
 #define        XFS_LI_ICREATE          0x123f
 #define        XFS_LI_RUI              0x1240  /* rmap update intent */
 #define        XFS_LI_RUD              0x1241
+#define        XFS_LI_CUI              0x1242  /* refcount update intent */
+#define        XFS_LI_CUD              0x1243
+#define        XFS_LI_BUI              0x1244  /* bmbt update intent */
+#define        XFS_LI_BUD              0x1245
 
 #define XFS_LI_TYPE_DESC \
        { XFS_LI_EFI,           "XFS_LI_EFI" }, \
@@ -242,7 +250,11 @@ typedef struct xfs_trans_header {
        { XFS_LI_QUOTAOFF,      "XFS_LI_QUOTAOFF" }, \
        { XFS_LI_ICREATE,       "XFS_LI_ICREATE" }, \
        { XFS_LI_RUI,           "XFS_LI_RUI" }, \
-       { XFS_LI_RUD,           "XFS_LI_RUD" }
+       { XFS_LI_RUD,           "XFS_LI_RUD" }, \
+       { XFS_LI_CUI,           "XFS_LI_CUI" }, \
+       { XFS_LI_CUD,           "XFS_LI_CUD" }, \
+       { XFS_LI_BUI,           "XFS_LI_BUI" }, \
+       { XFS_LI_BUD,           "XFS_LI_BUD" }
 
 /*
  * Inode Log Item Format definitions.
@@ -411,7 +423,8 @@ struct xfs_log_dinode {
        __uint64_t      di_changecount; /* number of attribute changes */
        xfs_lsn_t       di_lsn;         /* flush sequence */
        __uint64_t      di_flags2;      /* more random flags */
-       __uint8_t       di_pad2[16];    /* more padding for future expansion */
+       __uint32_t      di_cowextsize;  /* basic cow extent size for file */
+       __uint8_t       di_pad2[12];    /* more padding for future expansion */
 
        /* fields only written to during inode creation */
        xfs_ictimestamp_t di_crtime;    /* time created */
@@ -622,8 +635,11 @@ struct xfs_map_extent {
 
 /* rmap me_flags: upper bits are flags, lower byte is type code */
 #define XFS_RMAP_EXTENT_MAP            1
+#define XFS_RMAP_EXTENT_MAP_SHARED     2
 #define XFS_RMAP_EXTENT_UNMAP          3
+#define XFS_RMAP_EXTENT_UNMAP_SHARED   4
 #define XFS_RMAP_EXTENT_CONVERT                5
+#define XFS_RMAP_EXTENT_CONVERT_SHARED 6
 #define XFS_RMAP_EXTENT_ALLOC          7
 #define XFS_RMAP_EXTENT_FREE           8
 #define XFS_RMAP_EXTENT_TYPE_MASK      0xFF
@@ -670,6 +686,102 @@ struct xfs_rud_log_format {
        __uint64_t              rud_rui_id;     /* id of corresponding rui */
 };
 
+/*
+ * CUI/CUD (refcount update) log format definitions
+ */
+struct xfs_phys_extent {
+       __uint64_t              pe_startblock;
+       __uint32_t              pe_len;
+       __uint32_t              pe_flags;
+};
+
+/* refcount pe_flags: upper bits are flags, lower byte is type code */
+/* Type codes are taken directly from enum xfs_refcount_intent_type. */
+#define XFS_REFCOUNT_EXTENT_TYPE_MASK  0xFF
+
+#define XFS_REFCOUNT_EXTENT_FLAGS      (XFS_REFCOUNT_EXTENT_TYPE_MASK)
+
+/*
+ * This is the structure used to lay out a cui log item in the
+ * log.  The cui_extents field is a variable size array whose
+ * size is given by cui_nextents.
+ */
+struct xfs_cui_log_format {
+       __uint16_t              cui_type;       /* cui log item type */
+       __uint16_t              cui_size;       /* size of this item */
+       __uint32_t              cui_nextents;   /* # extents to free */
+       __uint64_t              cui_id;         /* cui identifier */
+       struct xfs_phys_extent  cui_extents[];  /* array of extents */
+};
+
+static inline size_t
+xfs_cui_log_format_sizeof(
+       unsigned int            nr)
+{
+       return sizeof(struct xfs_cui_log_format) +
+                       nr * sizeof(struct xfs_phys_extent);
+}
+
+/*
+ * This is the structure used to lay out a cud log item in the
+ * log.  The cud_extents array is a variable size array whose
+ * size is given by cud_nextents;
+ */
+struct xfs_cud_log_format {
+       __uint16_t              cud_type;       /* cud log item type */
+       __uint16_t              cud_size;       /* size of this item */
+       __uint32_t              __pad;
+       __uint64_t              cud_cui_id;     /* id of corresponding cui */
+};
+
+/*
+ * BUI/BUD (inode block mapping) log format definitions
+ */
+
+/* bmbt me_flags: upper bits are flags, lower byte is type code */
+/* Type codes are taken directly from enum xfs_bmap_intent_type. */
+#define XFS_BMAP_EXTENT_TYPE_MASK      0xFF
+
+#define XFS_BMAP_EXTENT_ATTR_FORK      (1U << 31)
+#define XFS_BMAP_EXTENT_UNWRITTEN      (1U << 30)
+
+#define XFS_BMAP_EXTENT_FLAGS          (XFS_BMAP_EXTENT_TYPE_MASK | \
+                                        XFS_BMAP_EXTENT_ATTR_FORK | \
+                                        XFS_BMAP_EXTENT_UNWRITTEN)
+
+/*
+ * This is the structure used to lay out an bui log item in the
+ * log.  The bui_extents field is a variable size array whose
+ * size is given by bui_nextents.
+ */
+struct xfs_bui_log_format {
+       __uint16_t              bui_type;       /* bui log item type */
+       __uint16_t              bui_size;       /* size of this item */
+       __uint32_t              bui_nextents;   /* # extents to free */
+       __uint64_t              bui_id;         /* bui identifier */
+       struct xfs_map_extent   bui_extents[];  /* array of extents to bmap */
+};
+
+static inline size_t
+xfs_bui_log_format_sizeof(
+       unsigned int            nr)
+{
+       return sizeof(struct xfs_bui_log_format) +
+                       nr * sizeof(struct xfs_map_extent);
+}
+
+/*
+ * This is the structure used to lay out an bud log item in the
+ * log.  The bud_extents array is a variable size array whose
+ * size is given by bud_nextents;
+ */
+struct xfs_bud_log_format {
+       __uint16_t              bud_type;       /* bud log item type */
+       __uint16_t              bud_size;       /* size of this item */
+       __uint32_t              __pad;
+       __uint64_t              bud_bui_id;     /* id of corresponding bui */
+};
+
 /*
  * Dquot Log format definitions.
  *
diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
new file mode 100644 (file)
index 0000000..b177ef3
--- /dev/null
@@ -0,0 +1,1698 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_sb.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_error.h"
+#include "xfs_trace.h"
+#include "xfs_cksum.h"
+#include "xfs_trans.h"
+#include "xfs_bit.h"
+#include "xfs_refcount.h"
+#include "xfs_rmap.h"
+
+/* Allowable refcount adjustment amounts. */
+enum xfs_refc_adjust_op {
+       XFS_REFCOUNT_ADJUST_INCREASE    = 1,
+       XFS_REFCOUNT_ADJUST_DECREASE    = -1,
+       XFS_REFCOUNT_ADJUST_COW_ALLOC   = 0,
+       XFS_REFCOUNT_ADJUST_COW_FREE    = -1,
+};
+
+STATIC int __xfs_refcount_cow_alloc(struct xfs_btree_cur *rcur,
+               xfs_agblock_t agbno, xfs_extlen_t aglen,
+               struct xfs_defer_ops *dfops);
+STATIC int __xfs_refcount_cow_free(struct xfs_btree_cur *rcur,
+               xfs_agblock_t agbno, xfs_extlen_t aglen,
+               struct xfs_defer_ops *dfops);
+
+/*
+ * Look up the first record less than or equal to [bno, len] in the btree
+ * given by cur.
+ */
+int
+xfs_refcount_lookup_le(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       int                     *stat)
+{
+       trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_private.a.agno, bno,
+                       XFS_LOOKUP_LE);
+       cur->bc_rec.rc.rc_startblock = bno;
+       cur->bc_rec.rc.rc_blockcount = 0;
+       return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
+}
+
+/*
+ * Look up the first record greater than or equal to [bno, len] in the btree
+ * given by cur.
+ */
+int
+xfs_refcount_lookup_ge(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       int                     *stat)
+{
+       trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_private.a.agno, bno,
+                       XFS_LOOKUP_GE);
+       cur->bc_rec.rc.rc_startblock = bno;
+       cur->bc_rec.rc.rc_blockcount = 0;
+       return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
+}
+
+/* Convert on-disk record to in-core format. */
+static inline void
+xfs_refcount_btrec_to_irec(
+       union xfs_btree_rec             *rec,
+       struct xfs_refcount_irec        *irec)
+{
+       irec->rc_startblock = be32_to_cpu(rec->refc.rc_startblock);
+       irec->rc_blockcount = be32_to_cpu(rec->refc.rc_blockcount);
+       irec->rc_refcount = be32_to_cpu(rec->refc.rc_refcount);
+}
+
+/*
+ * Get the data from the pointed-to record.
+ */
+int
+xfs_refcount_get_rec(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *irec,
+       int                             *stat)
+{
+       union xfs_btree_rec             *rec;
+       int                             error;
+
+       error = xfs_btree_get_rec(cur, &rec, stat);
+       if (!error && *stat == 1) {
+               xfs_refcount_btrec_to_irec(rec, irec);
+               trace_xfs_refcount_get(cur->bc_mp, cur->bc_private.a.agno,
+                               irec);
+       }
+       return error;
+}
+
+/*
+ * Update the record referred to by cur to the value given
+ * by [bno, len, refcount].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int
+xfs_refcount_update(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *irec)
+{
+       union xfs_btree_rec     rec;
+       int                     error;
+
+       trace_xfs_refcount_update(cur->bc_mp, cur->bc_private.a.agno, irec);
+       rec.refc.rc_startblock = cpu_to_be32(irec->rc_startblock);
+       rec.refc.rc_blockcount = cpu_to_be32(irec->rc_blockcount);
+       rec.refc.rc_refcount = cpu_to_be32(irec->rc_refcount);
+       error = xfs_btree_update(cur, &rec);
+       if (error)
+               trace_xfs_refcount_update_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Insert the record referred to by cur to the value given
+ * by [bno, len, refcount].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int
+xfs_refcount_insert(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *irec,
+       int                             *i)
+{
+       int                             error;
+
+       trace_xfs_refcount_insert(cur->bc_mp, cur->bc_private.a.agno, irec);
+       cur->bc_rec.rc.rc_startblock = irec->rc_startblock;
+       cur->bc_rec.rc.rc_blockcount = irec->rc_blockcount;
+       cur->bc_rec.rc.rc_refcount = irec->rc_refcount;
+       error = xfs_btree_insert(cur, i);
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, *i == 1, out_error);
+out_error:
+       if (error)
+               trace_xfs_refcount_insert_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Remove the record referred to by cur, then set the pointer to the spot
+ * where the record could be re-inserted, in case we want to increment or
+ * decrement the cursor.
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int
+xfs_refcount_delete(
+       struct xfs_btree_cur    *cur,
+       int                     *i)
+{
+       struct xfs_refcount_irec        irec;
+       int                     found_rec;
+       int                     error;
+
+       error = xfs_refcount_get_rec(cur, &irec, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+       trace_xfs_refcount_delete(cur->bc_mp, cur->bc_private.a.agno, &irec);
+       error = xfs_btree_delete(cur, i);
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, *i == 1, out_error);
+       if (error)
+               goto out_error;
+       error = xfs_refcount_lookup_ge(cur, irec.rc_startblock, &found_rec);
+out_error:
+       if (error)
+               trace_xfs_refcount_delete_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Adjusting the Reference Count
+ *
+ * As stated elsewhere, the reference count btree (refcbt) stores
+ * >1 reference counts for extents of physical blocks.  In this
+ * operation, we're either raising or lowering the reference count of
+ * some subrange stored in the tree:
+ *
+ *      <------ adjustment range ------>
+ * ----+   +---+-----+ +--+--------+---------
+ *  2  |   | 3 |  4  | |17|   55   |   10
+ * ----+   +---+-----+ +--+--------+---------
+ * X axis is physical blocks number;
+ * reference counts are the numbers inside the rectangles
+ *
+ * The first thing we need to do is to ensure that there are no
+ * refcount extents crossing either boundary of the range to be
+ * adjusted.  For any extent that does cross a boundary, split it into
+ * two extents so that we can increment the refcount of one of the
+ * pieces later:
+ *
+ *      <------ adjustment range ------>
+ * ----+   +---+-----+ +--+--------+----+----
+ *  2  |   | 3 |  2  | |17|   55   | 10 | 10
+ * ----+   +---+-----+ +--+--------+----+----
+ *
+ * For this next step, let's assume that all the physical blocks in
+ * the adjustment range are mapped to a file and are therefore in use
+ * at least once.  Therefore, we can infer that any gap in the
+ * refcount tree within the adjustment range represents a physical
+ * extent with refcount == 1:
+ *
+ *      <------ adjustment range ------>
+ * ----+---+---+-----+-+--+--------+----+----
+ *  2  |"1"| 3 |  2  |1|17|   55   | 10 | 10
+ * ----+---+---+-----+-+--+--------+----+----
+ *      ^
+ *
+ * For each extent that falls within the interval range, figure out
+ * which extent is to the left or the right of that extent.  Now we
+ * have a left, current, and right extent.  If the new reference count
+ * of the center extent enables us to merge left, center, and right
+ * into one record covering all three, do so.  If the center extent is
+ * at the left end of the range, abuts the left extent, and its new
+ * reference count matches the left extent's record, then merge them.
+ * If the center extent is at the right end of the range, abuts the
+ * right extent, and the reference counts match, merge those.  In the
+ * example, we can left merge (assuming an increment operation):
+ *
+ *      <------ adjustment range ------>
+ * --------+---+-----+-+--+--------+----+----
+ *    2    | 3 |  2  |1|17|   55   | 10 | 10
+ * --------+---+-----+-+--+--------+----+----
+ *          ^
+ *
+ * For all other extents within the range, adjust the reference count
+ * or delete it if the refcount falls below 2.  If we were
+ * incrementing, the end result looks like this:
+ *
+ *      <------ adjustment range ------>
+ * --------+---+-----+-+--+--------+----+----
+ *    2    | 4 |  3  |2|18|   56   | 11 | 10
+ * --------+---+-----+-+--+--------+----+----
+ *
+ * The result of a decrement operation looks as such:
+ *
+ *      <------ adjustment range ------>
+ * ----+   +---+       +--+--------+----+----
+ *  2  |   | 2 |       |16|   54   |  9 | 10
+ * ----+   +---+       +--+--------+----+----
+ *      DDDD    111111DD
+ *
+ * The blocks marked "D" are freed; the blocks marked "1" are only
+ * referenced once and therefore the record is removed from the
+ * refcount btree.
+ */
+
+/* Next block after this extent. */
+static inline xfs_agblock_t
+xfs_refc_next(
+       struct xfs_refcount_irec        *rc)
+{
+       return rc->rc_startblock + rc->rc_blockcount;
+}
+
+/*
+ * Split a refcount extent that crosses agbno.
+ */
+STATIC int
+xfs_refcount_split_extent(
+       struct xfs_btree_cur            *cur,
+       xfs_agblock_t                   agbno,
+       bool                            *shape_changed)
+{
+       struct xfs_refcount_irec        rcext, tmp;
+       int                             found_rec;
+       int                             error;
+
+       *shape_changed = false;
+       error = xfs_refcount_lookup_le(cur, agbno, &found_rec);
+       if (error)
+               goto out_error;
+       if (!found_rec)
+               return 0;
+
+       error = xfs_refcount_get_rec(cur, &rcext, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+       if (rcext.rc_startblock == agbno || xfs_refc_next(&rcext) <= agbno)
+               return 0;
+
+       *shape_changed = true;
+       trace_xfs_refcount_split_extent(cur->bc_mp, cur->bc_private.a.agno,
+                       &rcext, agbno);
+
+       /* Establish the right extent. */
+       tmp = rcext;
+       tmp.rc_startblock = agbno;
+       tmp.rc_blockcount -= (agbno - rcext.rc_startblock);
+       error = xfs_refcount_update(cur, &tmp);
+       if (error)
+               goto out_error;
+
+       /* Insert the left extent. */
+       tmp = rcext;
+       tmp.rc_blockcount = agbno - rcext.rc_startblock;
+       error = xfs_refcount_insert(cur, &tmp, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+       return error;
+
+out_error:
+       trace_xfs_refcount_split_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Merge the left, center, and right extents.
+ */
+STATIC int
+xfs_refcount_merge_center_extents(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *left,
+       struct xfs_refcount_irec        *center,
+       struct xfs_refcount_irec        *right,
+       unsigned long long              extlen,
+       xfs_agblock_t                   *agbno,
+       xfs_extlen_t                    *aglen)
+{
+       int                             error;
+       int                             found_rec;
+
+       trace_xfs_refcount_merge_center_extents(cur->bc_mp,
+                       cur->bc_private.a.agno, left, center, right);
+
+       /*
+        * Make sure the center and right extents are not in the btree.
+        * If the center extent was synthesized, the first delete call
+        * removes the right extent and we skip the second deletion.
+        * If center and right were in the btree, then the first delete
+        * call removes the center and the second one removes the right
+        * extent.
+        */
+       error = xfs_refcount_lookup_ge(cur, center->rc_startblock,
+                       &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       error = xfs_refcount_delete(cur, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       if (center->rc_refcount > 1) {
+               error = xfs_refcount_delete(cur, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+       }
+
+       /* Enlarge the left extent. */
+       error = xfs_refcount_lookup_le(cur, left->rc_startblock,
+                       &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       left->rc_blockcount = extlen;
+       error = xfs_refcount_update(cur, left);
+       if (error)
+               goto out_error;
+
+       *aglen = 0;
+       return error;
+
+out_error:
+       trace_xfs_refcount_merge_center_extents_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Merge with the left extent.
+ */
+STATIC int
+xfs_refcount_merge_left_extent(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *left,
+       struct xfs_refcount_irec        *cleft,
+       xfs_agblock_t                   *agbno,
+       xfs_extlen_t                    *aglen)
+{
+       int                             error;
+       int                             found_rec;
+
+       trace_xfs_refcount_merge_left_extent(cur->bc_mp,
+                       cur->bc_private.a.agno, left, cleft);
+
+       /* If the extent at agbno (cleft) wasn't synthesized, remove it. */
+       if (cleft->rc_refcount > 1) {
+               error = xfs_refcount_lookup_le(cur, cleft->rc_startblock,
+                               &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+
+               error = xfs_refcount_delete(cur, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+       }
+
+       /* Enlarge the left extent. */
+       error = xfs_refcount_lookup_le(cur, left->rc_startblock,
+                       &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       left->rc_blockcount += cleft->rc_blockcount;
+       error = xfs_refcount_update(cur, left);
+       if (error)
+               goto out_error;
+
+       *agbno += cleft->rc_blockcount;
+       *aglen -= cleft->rc_blockcount;
+       return error;
+
+out_error:
+       trace_xfs_refcount_merge_left_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Merge with the right extent.
+ */
+STATIC int
+xfs_refcount_merge_right_extent(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *right,
+       struct xfs_refcount_irec        *cright,
+       xfs_agblock_t                   *agbno,
+       xfs_extlen_t                    *aglen)
+{
+       int                             error;
+       int                             found_rec;
+
+       trace_xfs_refcount_merge_right_extent(cur->bc_mp,
+                       cur->bc_private.a.agno, cright, right);
+
+       /*
+        * If the extent ending at agbno+aglen (cright) wasn't synthesized,
+        * remove it.
+        */
+       if (cright->rc_refcount > 1) {
+               error = xfs_refcount_lookup_le(cur, cright->rc_startblock,
+                       &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+
+               error = xfs_refcount_delete(cur, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+       }
+
+       /* Enlarge the right extent. */
+       error = xfs_refcount_lookup_le(cur, right->rc_startblock,
+                       &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       right->rc_startblock -= cright->rc_blockcount;
+       right->rc_blockcount += cright->rc_blockcount;
+       error = xfs_refcount_update(cur, right);
+       if (error)
+               goto out_error;
+
+       *aglen -= cright->rc_blockcount;
+       return error;
+
+out_error:
+       trace_xfs_refcount_merge_right_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+#define XFS_FIND_RCEXT_SHARED  1
+#define XFS_FIND_RCEXT_COW     2
+/*
+ * Find the left extent and the one after it (cleft).  This function assumes
+ * that we've already split any extent crossing agbno.
+ */
+STATIC int
+xfs_refcount_find_left_extents(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *left,
+       struct xfs_refcount_irec        *cleft,
+       xfs_agblock_t                   agbno,
+       xfs_extlen_t                    aglen,
+       int                             flags)
+{
+       struct xfs_refcount_irec        tmp;
+       int                             error;
+       int                             found_rec;
+
+       left->rc_startblock = cleft->rc_startblock = NULLAGBLOCK;
+       error = xfs_refcount_lookup_le(cur, agbno - 1, &found_rec);
+       if (error)
+               goto out_error;
+       if (!found_rec)
+               return 0;
+
+       error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       if (xfs_refc_next(&tmp) != agbno)
+               return 0;
+       if ((flags & XFS_FIND_RCEXT_SHARED) && tmp.rc_refcount < 2)
+               return 0;
+       if ((flags & XFS_FIND_RCEXT_COW) && tmp.rc_refcount > 1)
+               return 0;
+       /* We have a left extent; retrieve (or invent) the next right one */
+       *left = tmp;
+
+       error = xfs_btree_increment(cur, 0, &found_rec);
+       if (error)
+               goto out_error;
+       if (found_rec) {
+               error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+
+               /* if tmp starts at the end of our range, just use that */
+               if (tmp.rc_startblock == agbno)
+                       *cleft = tmp;
+               else {
+                       /*
+                        * There's a gap in the refcntbt at the start of the
+                        * range we're interested in (refcount == 1) so
+                        * synthesize the implied extent and pass it back.
+                        * We assume here that the agbno/aglen range was
+                        * passed in from a data fork extent mapping and
+                        * therefore is allocated to exactly one owner.
+                        */
+                       cleft->rc_startblock = agbno;
+                       cleft->rc_blockcount = min(aglen,
+                                       tmp.rc_startblock - agbno);
+                       cleft->rc_refcount = 1;
+               }
+       } else {
+               /*
+                * No extents, so pretend that there's one covering the whole
+                * range.
+                */
+               cleft->rc_startblock = agbno;
+               cleft->rc_blockcount = aglen;
+               cleft->rc_refcount = 1;
+       }
+       trace_xfs_refcount_find_left_extent(cur->bc_mp, cur->bc_private.a.agno,
+                       left, cleft, agbno);
+       return error;
+
+out_error:
+       trace_xfs_refcount_find_left_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Find the right extent and the one before it (cright).  This function
+ * assumes that we've already split any extents crossing agbno + aglen.
+ */
+STATIC int
+xfs_refcount_find_right_extents(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *right,
+       struct xfs_refcount_irec        *cright,
+       xfs_agblock_t                   agbno,
+       xfs_extlen_t                    aglen,
+       int                             flags)
+{
+       struct xfs_refcount_irec        tmp;
+       int                             error;
+       int                             found_rec;
+
+       right->rc_startblock = cright->rc_startblock = NULLAGBLOCK;
+       error = xfs_refcount_lookup_ge(cur, agbno + aglen, &found_rec);
+       if (error)
+               goto out_error;
+       if (!found_rec)
+               return 0;
+
+       error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       if (tmp.rc_startblock != agbno + aglen)
+               return 0;
+       if ((flags & XFS_FIND_RCEXT_SHARED) && tmp.rc_refcount < 2)
+               return 0;
+       if ((flags & XFS_FIND_RCEXT_COW) && tmp.rc_refcount > 1)
+               return 0;
+       /* We have a right extent; retrieve (or invent) the next left one */
+       *right = tmp;
+
+       error = xfs_btree_decrement(cur, 0, &found_rec);
+       if (error)
+               goto out_error;
+       if (found_rec) {
+               error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+
+               /* if tmp ends at the end of our range, just use that */
+               if (xfs_refc_next(&tmp) == agbno + aglen)
+                       *cright = tmp;
+               else {
+                       /*
+                        * There's a gap in the refcntbt at the end of the
+                        * range we're interested in (refcount == 1) so
+                        * create the implied extent and pass it back.
+                        * We assume here that the agbno/aglen range was
+                        * passed in from a data fork extent mapping and
+                        * therefore is allocated to exactly one owner.
+                        */
+                       cright->rc_startblock = max(agbno, xfs_refc_next(&tmp));
+                       cright->rc_blockcount = right->rc_startblock -
+                                       cright->rc_startblock;
+                       cright->rc_refcount = 1;
+               }
+       } else {
+               /*
+                * No extents, so pretend that there's one covering the whole
+                * range.
+                */
+               cright->rc_startblock = agbno;
+               cright->rc_blockcount = aglen;
+               cright->rc_refcount = 1;
+       }
+       trace_xfs_refcount_find_right_extent(cur->bc_mp, cur->bc_private.a.agno,
+                       cright, right, agbno + aglen);
+       return error;
+
+out_error:
+       trace_xfs_refcount_find_right_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/* Is this extent valid? */
+static inline bool
+xfs_refc_valid(
+       struct xfs_refcount_irec        *rc)
+{
+       return rc->rc_startblock != NULLAGBLOCK;
+}
+
+/*
+ * Try to merge with any extents on the boundaries of the adjustment range.
+ */
+STATIC int
+xfs_refcount_merge_extents(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           *agbno,
+       xfs_extlen_t            *aglen,
+       enum xfs_refc_adjust_op adjust,
+       int                     flags,
+       bool                    *shape_changed)
+{
+       struct xfs_refcount_irec        left = {0}, cleft = {0};
+       struct xfs_refcount_irec        cright = {0}, right = {0};
+       int                             error;
+       unsigned long long              ulen;
+       bool                            cequal;
+
+       *shape_changed = false;
+       /*
+        * Find the extent just below agbno [left], just above agbno [cleft],
+        * just below (agbno + aglen) [cright], and just above (agbno + aglen)
+        * [right].
+        */
+       error = xfs_refcount_find_left_extents(cur, &left, &cleft, *agbno,
+                       *aglen, flags);
+       if (error)
+               return error;
+       error = xfs_refcount_find_right_extents(cur, &right, &cright, *agbno,
+                       *aglen, flags);
+       if (error)
+               return error;
+
+       /* No left or right extent to merge; exit. */
+       if (!xfs_refc_valid(&left) && !xfs_refc_valid(&right))
+               return 0;
+
+       cequal = (cleft.rc_startblock == cright.rc_startblock) &&
+                (cleft.rc_blockcount == cright.rc_blockcount);
+
+       /* Try to merge left, cleft, and right.  cleft must == cright. */
+       ulen = (unsigned long long)left.rc_blockcount + cleft.rc_blockcount +
+                       right.rc_blockcount;
+       if (xfs_refc_valid(&left) && xfs_refc_valid(&right) &&
+           xfs_refc_valid(&cleft) && xfs_refc_valid(&cright) && cequal &&
+           left.rc_refcount == cleft.rc_refcount + adjust &&
+           right.rc_refcount == cleft.rc_refcount + adjust &&
+           ulen < MAXREFCEXTLEN) {
+               *shape_changed = true;
+               return xfs_refcount_merge_center_extents(cur, &left, &cleft,
+                               &right, ulen, agbno, aglen);
+       }
+
+       /* Try to merge left and cleft. */
+       ulen = (unsigned long long)left.rc_blockcount + cleft.rc_blockcount;
+       if (xfs_refc_valid(&left) && xfs_refc_valid(&cleft) &&
+           left.rc_refcount == cleft.rc_refcount + adjust &&
+           ulen < MAXREFCEXTLEN) {
+               *shape_changed = true;
+               error = xfs_refcount_merge_left_extent(cur, &left, &cleft,
+                               agbno, aglen);
+               if (error)
+                       return error;
+
+               /*
+                * If we just merged left + cleft and cleft == cright,
+                * we no longer have a cright to merge with right.  We're done.
+                */
+               if (cequal)
+                       return 0;
+       }
+
+       /* Try to merge cright and right. */
+       ulen = (unsigned long long)right.rc_blockcount + cright.rc_blockcount;
+       if (xfs_refc_valid(&right) && xfs_refc_valid(&cright) &&
+           right.rc_refcount == cright.rc_refcount + adjust &&
+           ulen < MAXREFCEXTLEN) {
+               *shape_changed = true;
+               return xfs_refcount_merge_right_extent(cur, &right, &cright,
+                               agbno, aglen);
+       }
+
+       return error;
+}
+
+/*
+ * While we're adjusting the refcounts records of an extent, we have
+ * to keep an eye on the number of extents we're dirtying -- run too
+ * many in a single transaction and we'll exceed the transaction's
+ * reservation and crash the fs.  Each record adds 12 bytes to the
+ * log (plus any key updates) so we'll conservatively assume 24 bytes
+ * per record.  We must also leave space for btree splits on both ends
+ * of the range and space for the CUD and a new CUI.
+ *
+ * XXX: This is a pretty hand-wavy estimate.  The penalty for guessing
+ * true incorrectly is a shutdown FS; the penalty for guessing false
+ * incorrectly is more transaction rolls than might be necessary.
+ * Be conservative here.
+ */
+static bool
+xfs_refcount_still_have_space(
+       struct xfs_btree_cur            *cur)
+{
+       unsigned long                   overhead;
+
+       overhead = cur->bc_private.a.priv.refc.shape_changes *
+                       xfs_allocfree_log_count(cur->bc_mp, 1);
+       overhead *= cur->bc_mp->m_sb.sb_blocksize;
+
+       /*
+        * Only allow 2 refcount extent updates per transaction if the
+        * refcount continue update "error" has been injected.
+        */
+       if (cur->bc_private.a.priv.refc.nr_ops > 2 &&
+           XFS_TEST_ERROR(false, cur->bc_mp,
+                       XFS_ERRTAG_REFCOUNT_CONTINUE_UPDATE,
+                       XFS_RANDOM_REFCOUNT_CONTINUE_UPDATE))
+               return false;
+
+       if (cur->bc_private.a.priv.refc.nr_ops == 0)
+               return true;
+       else if (overhead > cur->bc_tp->t_log_res)
+               return false;
+       return  cur->bc_tp->t_log_res - overhead >
+               cur->bc_private.a.priv.refc.nr_ops * 32;
+}
+
+/*
+ * Adjust the refcounts of middle extents.  At this point we should have
+ * split extents that crossed the adjustment range; merged with adjacent
+ * extents; and updated agbno/aglen to reflect the merges.  Therefore,
+ * all we have to do is update the extents inside [agbno, agbno + aglen].
+ */
+STATIC int
+xfs_refcount_adjust_extents(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           *agbno,
+       xfs_extlen_t            *aglen,
+       enum xfs_refc_adjust_op adj,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_owner_info   *oinfo)
+{
+       struct xfs_refcount_irec        ext, tmp;
+       int                             error;
+       int                             found_rec, found_tmp;
+       xfs_fsblock_t                   fsbno;
+
+       /* Merging did all the work already. */
+       if (*aglen == 0)
+               return 0;
+
+       error = xfs_refcount_lookup_ge(cur, *agbno, &found_rec);
+       if (error)
+               goto out_error;
+
+       while (*aglen > 0 && xfs_refcount_still_have_space(cur)) {
+               error = xfs_refcount_get_rec(cur, &ext, &found_rec);
+               if (error)
+                       goto out_error;
+               if (!found_rec) {
+                       ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks;
+                       ext.rc_blockcount = 0;
+                       ext.rc_refcount = 0;
+               }
+
+               /*
+                * Deal with a hole in the refcount tree; if a file maps to
+                * these blocks and there's no refcountbt record, pretend that
+                * there is one with refcount == 1.
+                */
+               if (ext.rc_startblock != *agbno) {
+                       tmp.rc_startblock = *agbno;
+                       tmp.rc_blockcount = min(*aglen,
+                                       ext.rc_startblock - *agbno);
+                       tmp.rc_refcount = 1 + adj;
+                       trace_xfs_refcount_modify_extent(cur->bc_mp,
+                                       cur->bc_private.a.agno, &tmp);
+
+                       /*
+                        * Either cover the hole (increment) or
+                        * delete the range (decrement).
+                        */
+                       if (tmp.rc_refcount) {
+                               error = xfs_refcount_insert(cur, &tmp,
+                                               &found_tmp);
+                               if (error)
+                                       goto out_error;
+                               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                                               found_tmp == 1, out_error);
+                               cur->bc_private.a.priv.refc.nr_ops++;
+                       } else {
+                               fsbno = XFS_AGB_TO_FSB(cur->bc_mp,
+                                               cur->bc_private.a.agno,
+                                               tmp.rc_startblock);
+                               xfs_bmap_add_free(cur->bc_mp, dfops, fsbno,
+                                               tmp.rc_blockcount, oinfo);
+                       }
+
+                       (*agbno) += tmp.rc_blockcount;
+                       (*aglen) -= tmp.rc_blockcount;
+
+                       error = xfs_refcount_lookup_ge(cur, *agbno,
+                                       &found_rec);
+                       if (error)
+                               goto out_error;
+               }
+
+               /* Stop if there's nothing left to modify */
+               if (*aglen == 0 || !xfs_refcount_still_have_space(cur))
+                       break;
+
+               /*
+                * Adjust the reference count and either update the tree
+                * (incr) or free the blocks (decr).
+                */
+               if (ext.rc_refcount == MAXREFCOUNT)
+                       goto skip;
+               ext.rc_refcount += adj;
+               trace_xfs_refcount_modify_extent(cur->bc_mp,
+                               cur->bc_private.a.agno, &ext);
+               if (ext.rc_refcount > 1) {
+                       error = xfs_refcount_update(cur, &ext);
+                       if (error)
+                               goto out_error;
+                       cur->bc_private.a.priv.refc.nr_ops++;
+               } else if (ext.rc_refcount == 1) {
+                       error = xfs_refcount_delete(cur, &found_rec);
+                       if (error)
+                               goto out_error;
+                       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                                       found_rec == 1, out_error);
+                       cur->bc_private.a.priv.refc.nr_ops++;
+                       goto advloop;
+               } else {
+                       fsbno = XFS_AGB_TO_FSB(cur->bc_mp,
+                                       cur->bc_private.a.agno,
+                                       ext.rc_startblock);
+                       xfs_bmap_add_free(cur->bc_mp, dfops, fsbno,
+                                       ext.rc_blockcount, oinfo);
+               }
+
+skip:
+               error = xfs_btree_increment(cur, 0, &found_rec);
+               if (error)
+                       goto out_error;
+
+advloop:
+               (*agbno) += ext.rc_blockcount;
+               (*aglen) -= ext.rc_blockcount;
+       }
+
+       return error;
+out_error:
+       trace_xfs_refcount_modify_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/* Adjust the reference count of a range of AG blocks. */
+STATIC int
+xfs_refcount_adjust(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       xfs_agblock_t           *new_agbno,
+       xfs_extlen_t            *new_aglen,
+       enum xfs_refc_adjust_op adj,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_owner_info   *oinfo)
+{
+       bool                    shape_changed;
+       int                     shape_changes = 0;
+       int                     error;
+
+       *new_agbno = agbno;
+       *new_aglen = aglen;
+       if (adj == XFS_REFCOUNT_ADJUST_INCREASE)
+               trace_xfs_refcount_increase(cur->bc_mp, cur->bc_private.a.agno,
+                               agbno, aglen);
+       else
+               trace_xfs_refcount_decrease(cur->bc_mp, cur->bc_private.a.agno,
+                               agbno, aglen);
+
+       /*
+        * Ensure that no rcextents cross the boundary of the adjustment range.
+        */
+       error = xfs_refcount_split_extent(cur, agbno, &shape_changed);
+       if (error)
+               goto out_error;
+       if (shape_changed)
+               shape_changes++;
+
+       error = xfs_refcount_split_extent(cur, agbno + aglen, &shape_changed);
+       if (error)
+               goto out_error;
+       if (shape_changed)
+               shape_changes++;
+
+       /*
+        * Try to merge with the left or right extents of the range.
+        */
+       error = xfs_refcount_merge_extents(cur, new_agbno, new_aglen, adj,
+                       XFS_FIND_RCEXT_SHARED, &shape_changed);
+       if (error)
+               goto out_error;
+       if (shape_changed)
+               shape_changes++;
+       if (shape_changes)
+               cur->bc_private.a.priv.refc.shape_changes++;
+
+       /* Now that we've taken care of the ends, adjust the middle extents */
+       error = xfs_refcount_adjust_extents(cur, new_agbno, new_aglen,
+                       adj, dfops, oinfo);
+       if (error)
+               goto out_error;
+
+       return 0;
+
+out_error:
+       trace_xfs_refcount_adjust_error(cur->bc_mp, cur->bc_private.a.agno,
+                       error, _RET_IP_);
+       return error;
+}
+
+/* Clean up after calling xfs_refcount_finish_one. */
+void
+xfs_refcount_finish_one_cleanup(
+       struct xfs_trans        *tp,
+       struct xfs_btree_cur    *rcur,
+       int                     error)
+{
+       struct xfs_buf          *agbp;
+
+       if (rcur == NULL)
+               return;
+       agbp = rcur->bc_private.a.agbp;
+       xfs_btree_del_cursor(rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+       if (error)
+               xfs_trans_brelse(tp, agbp);
+}
+
+/*
+ * Process one of the deferred refcount operations.  We pass back the
+ * btree cursor to maintain our lock on the btree between calls.
+ * This saves time and eliminates a buffer deadlock between the
+ * superblock and the AGF because we'll always grab them in the same
+ * order.
+ */
+int
+xfs_refcount_finish_one(
+       struct xfs_trans                *tp,
+       struct xfs_defer_ops            *dfops,
+       enum xfs_refcount_intent_type   type,
+       xfs_fsblock_t                   startblock,
+       xfs_extlen_t                    blockcount,
+       xfs_fsblock_t                   *new_fsb,
+       xfs_extlen_t                    *new_len,
+       struct xfs_btree_cur            **pcur)
+{
+       struct xfs_mount                *mp = tp->t_mountp;
+       struct xfs_btree_cur            *rcur;
+       struct xfs_buf                  *agbp = NULL;
+       int                             error = 0;
+       xfs_agnumber_t                  agno;
+       xfs_agblock_t                   bno;
+       xfs_agblock_t                   new_agbno;
+       unsigned long                   nr_ops = 0;
+       int                             shape_changes = 0;
+
+       agno = XFS_FSB_TO_AGNO(mp, startblock);
+       ASSERT(agno != NULLAGNUMBER);
+       bno = XFS_FSB_TO_AGBNO(mp, startblock);
+
+       trace_xfs_refcount_deferred(mp, XFS_FSB_TO_AGNO(mp, startblock),
+                       type, XFS_FSB_TO_AGBNO(mp, startblock),
+                       blockcount);
+
+       if (XFS_TEST_ERROR(false, mp,
+                       XFS_ERRTAG_REFCOUNT_FINISH_ONE,
+                       XFS_RANDOM_REFCOUNT_FINISH_ONE))
+               return -EIO;
+
+       /*
+        * If we haven't gotten a cursor or the cursor AG doesn't match
+        * the startblock, get one now.
+        */
+       rcur = *pcur;
+       if (rcur != NULL && rcur->bc_private.a.agno != agno) {
+               nr_ops = rcur->bc_private.a.priv.refc.nr_ops;
+               shape_changes = rcur->bc_private.a.priv.refc.shape_changes;
+               xfs_refcount_finish_one_cleanup(tp, rcur, 0);
+               rcur = NULL;
+               *pcur = NULL;
+       }
+       if (rcur == NULL) {
+               error = xfs_alloc_read_agf(tp->t_mountp, tp, agno,
+                               XFS_ALLOC_FLAG_FREEING, &agbp);
+               if (error)
+                       return error;
+               if (!agbp)
+                       return -EFSCORRUPTED;
+
+               rcur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, dfops);
+               if (!rcur) {
+                       error = -ENOMEM;
+                       goto out_cur;
+               }
+               rcur->bc_private.a.priv.refc.nr_ops = nr_ops;
+               rcur->bc_private.a.priv.refc.shape_changes = shape_changes;
+       }
+       *pcur = rcur;
+
+       switch (type) {
+       case XFS_REFCOUNT_INCREASE:
+               error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno,
+                       new_len, XFS_REFCOUNT_ADJUST_INCREASE, dfops, NULL);
+               *new_fsb = XFS_AGB_TO_FSB(mp, agno, new_agbno);
+               break;
+       case XFS_REFCOUNT_DECREASE:
+               error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno,
+                       new_len, XFS_REFCOUNT_ADJUST_DECREASE, dfops, NULL);
+               *new_fsb = XFS_AGB_TO_FSB(mp, agno, new_agbno);
+               break;
+       case XFS_REFCOUNT_ALLOC_COW:
+               *new_fsb = startblock + blockcount;
+               *new_len = 0;
+               error = __xfs_refcount_cow_alloc(rcur, bno, blockcount, dfops);
+               break;
+       case XFS_REFCOUNT_FREE_COW:
+               *new_fsb = startblock + blockcount;
+               *new_len = 0;
+               error = __xfs_refcount_cow_free(rcur, bno, blockcount, dfops);
+               break;
+       default:
+               ASSERT(0);
+               error = -EFSCORRUPTED;
+       }
+       if (!error && *new_len > 0)
+               trace_xfs_refcount_finish_one_leftover(mp, agno, type,
+                               bno, blockcount, new_agbno, *new_len);
+       return error;
+
+out_cur:
+       xfs_trans_brelse(tp, agbp);
+
+       return error;
+}
+
+/*
+ * Record a refcount intent for later processing.
+ */
+static int
+__xfs_refcount_add(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       enum xfs_refcount_intent_type   type,
+       xfs_fsblock_t                   startblock,
+       xfs_extlen_t                    blockcount)
+{
+       struct xfs_refcount_intent      *ri;
+
+       trace_xfs_refcount_defer(mp, XFS_FSB_TO_AGNO(mp, startblock),
+                       type, XFS_FSB_TO_AGBNO(mp, startblock),
+                       blockcount);
+
+       ri = kmem_alloc(sizeof(struct xfs_refcount_intent),
+                       KM_SLEEP | KM_NOFS);
+       INIT_LIST_HEAD(&ri->ri_list);
+       ri->ri_type = type;
+       ri->ri_startblock = startblock;
+       ri->ri_blockcount = blockcount;
+
+       xfs_defer_add(dfops, XFS_DEFER_OPS_TYPE_REFCOUNT, &ri->ri_list);
+       return 0;
+}
+
+/*
+ * Increase the reference count of the blocks backing a file's extent.
+ */
+int
+xfs_refcount_increase_extent(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       struct xfs_bmbt_irec            *PREV)
+{
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_INCREASE,
+                       PREV->br_startblock, PREV->br_blockcount);
+}
+
+/*
+ * Decrease the reference count of the blocks backing a file's extent.
+ */
+int
+xfs_refcount_decrease_extent(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       struct xfs_bmbt_irec            *PREV)
+{
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_DECREASE,
+                       PREV->br_startblock, PREV->br_blockcount);
+}
+
+/*
+ * Given an AG extent, find the lowest-numbered run of shared blocks
+ * within that range and return the range in fbno/flen.  If
+ * find_end_of_shared is set, return the longest contiguous extent of
+ * shared blocks; if not, just return the first extent we find.  If no
+ * shared blocks are found, fbno and flen will be set to NULLAGBLOCK
+ * and 0, respectively.
+ */
+int
+xfs_refcount_find_shared(
+       struct xfs_btree_cur            *cur,
+       xfs_agblock_t                   agbno,
+       xfs_extlen_t                    aglen,
+       xfs_agblock_t                   *fbno,
+       xfs_extlen_t                    *flen,
+       bool                            find_end_of_shared)
+{
+       struct xfs_refcount_irec        tmp;
+       int                             i;
+       int                             have;
+       int                             error;
+
+       trace_xfs_refcount_find_shared(cur->bc_mp, cur->bc_private.a.agno,
+                       agbno, aglen);
+
+       /* By default, skip the whole range */
+       *fbno = NULLAGBLOCK;
+       *flen = 0;
+
+       /* Try to find a refcount extent that crosses the start */
+       error = xfs_refcount_lookup_le(cur, agbno, &have);
+       if (error)
+               goto out_error;
+       if (!have) {
+               /* No left extent, look at the next one */
+               error = xfs_btree_increment(cur, 0, &have);
+               if (error)
+                       goto out_error;
+               if (!have)
+                       goto done;
+       }
+       error = xfs_refcount_get_rec(cur, &tmp, &i);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, i == 1, out_error);
+
+       /* If the extent ends before the start, look at the next one */
+       if (tmp.rc_startblock + tmp.rc_blockcount <= agbno) {
+               error = xfs_btree_increment(cur, 0, &have);
+               if (error)
+                       goto out_error;
+               if (!have)
+                       goto done;
+               error = xfs_refcount_get_rec(cur, &tmp, &i);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, i == 1, out_error);
+       }
+
+       /* If the extent starts after the range we want, bail out */
+       if (tmp.rc_startblock >= agbno + aglen)
+               goto done;
+
+       /* We found the start of a shared extent! */
+       if (tmp.rc_startblock < agbno) {
+               tmp.rc_blockcount -= (agbno - tmp.rc_startblock);
+               tmp.rc_startblock = agbno;
+       }
+
+       *fbno = tmp.rc_startblock;
+       *flen = min(tmp.rc_blockcount, agbno + aglen - *fbno);
+       if (!find_end_of_shared)
+               goto done;
+
+       /* Otherwise, find the end of this shared extent */
+       while (*fbno + *flen < agbno + aglen) {
+               error = xfs_btree_increment(cur, 0, &have);
+               if (error)
+                       goto out_error;
+               if (!have)
+                       break;
+               error = xfs_refcount_get_rec(cur, &tmp, &i);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, i == 1, out_error);
+               if (tmp.rc_startblock >= agbno + aglen ||
+                   tmp.rc_startblock != *fbno + *flen)
+                       break;
+               *flen = min(*flen + tmp.rc_blockcount, agbno + aglen - *fbno);
+       }
+
+done:
+       trace_xfs_refcount_find_shared_result(cur->bc_mp,
+                       cur->bc_private.a.agno, *fbno, *flen);
+
+out_error:
+       if (error)
+               trace_xfs_refcount_find_shared_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Recovering CoW Blocks After a Crash
+ *
+ * Due to the way that the copy on write mechanism works, there's a window of
+ * opportunity in which we can lose track of allocated blocks during a crash.
+ * Because CoW uses delayed allocation in the in-core CoW fork, writeback
+ * causes blocks to be allocated and stored in the CoW fork.  The blocks are
+ * no longer in the free space btree but are not otherwise recorded anywhere
+ * until the write completes and the blocks are mapped into the file.  A crash
+ * in between allocation and remapping results in the replacement blocks being
+ * lost.  This situation is exacerbated by the CoW extent size hint because
+ * allocations can hang around for long time.
+ *
+ * However, there is a place where we can record these allocations before they
+ * become mappings -- the reference count btree.  The btree does not record
+ * extents with refcount == 1, so we can record allocations with a refcount of
+ * 1.  Blocks being used for CoW writeout cannot be shared, so there should be
+ * no conflict with shared block records.  These mappings should be created
+ * when we allocate blocks to the CoW fork and deleted when they're removed
+ * from the CoW fork.
+ *
+ * Minor nit: records for in-progress CoW allocations and records for shared
+ * extents must never be merged, to preserve the property that (except for CoW
+ * allocations) there are no refcount btree entries with refcount == 1.  The
+ * only time this could potentially happen is when unsharing a block that's
+ * adjacent to CoW allocations, so we must be careful to avoid this.
+ *
+ * At mount time we recover lost CoW allocations by searching the refcount
+ * btree for these refcount == 1 mappings.  These represent CoW allocations
+ * that were in progress at the time the filesystem went down, so we can free
+ * them to get the space back.
+ *
+ * This mechanism is superior to creating EFIs for unmapped CoW extents for
+ * several reasons -- first, EFIs pin the tail of the log and would have to be
+ * periodically relogged to avoid filling up the log.  Second, CoW completions
+ * will have to file an EFD and create new EFIs for whatever remains in the
+ * CoW fork; this partially takes care of (1) but extent-size reservations
+ * will have to periodically relog even if there's no writeout in progress.
+ * This can happen if the CoW extent size hint is set, which you really want.
+ * Third, EFIs cannot currently be automatically relogged into newer
+ * transactions to advance the log tail.  Fourth, stuffing the log full of
+ * EFIs places an upper bound on the number of CoW allocations that can be
+ * held filesystem-wide at any given time.  Recording them in the refcount
+ * btree doesn't require us to maintain any state in memory and doesn't pin
+ * the log.
+ */
+/*
+ * Adjust the refcounts of CoW allocations.  These allocations are "magic"
+ * in that they're not referenced anywhere else in the filesystem, so we
+ * stash them in the refcount btree with a refcount of 1 until either file
+ * remapping (or CoW cancellation) happens.
+ */
+STATIC int
+xfs_refcount_adjust_cow_extents(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       enum xfs_refc_adjust_op adj,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_owner_info   *oinfo)
+{
+       struct xfs_refcount_irec        ext, tmp;
+       int                             error;
+       int                             found_rec, found_tmp;
+
+       if (aglen == 0)
+               return 0;
+
+       /* Find any overlapping refcount records */
+       error = xfs_refcount_lookup_ge(cur, agbno, &found_rec);
+       if (error)
+               goto out_error;
+       error = xfs_refcount_get_rec(cur, &ext, &found_rec);
+       if (error)
+               goto out_error;
+       if (!found_rec) {
+               ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks +
+                               XFS_REFC_COW_START;
+               ext.rc_blockcount = 0;
+               ext.rc_refcount = 0;
+       }
+
+       switch (adj) {
+       case XFS_REFCOUNT_ADJUST_COW_ALLOC:
+               /* Adding a CoW reservation, there should be nothing here. */
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                               ext.rc_startblock >= agbno + aglen, out_error);
+
+               tmp.rc_startblock = agbno;
+               tmp.rc_blockcount = aglen;
+               tmp.rc_refcount = 1;
+               trace_xfs_refcount_modify_extent(cur->bc_mp,
+                               cur->bc_private.a.agno, &tmp);
+
+               error = xfs_refcount_insert(cur, &tmp,
+                               &found_tmp);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                               found_tmp == 1, out_error);
+               break;
+       case XFS_REFCOUNT_ADJUST_COW_FREE:
+               /* Removing a CoW reservation, there should be one extent. */
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                       ext.rc_startblock == agbno, out_error);
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                       ext.rc_blockcount == aglen, out_error);
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                       ext.rc_refcount == 1, out_error);
+
+               ext.rc_refcount = 0;
+               trace_xfs_refcount_modify_extent(cur->bc_mp,
+                               cur->bc_private.a.agno, &ext);
+               error = xfs_refcount_delete(cur, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                               found_rec == 1, out_error);
+               break;
+       default:
+               ASSERT(0);
+       }
+
+       return error;
+out_error:
+       trace_xfs_refcount_modify_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Add or remove refcount btree entries for CoW reservations.
+ */
+STATIC int
+xfs_refcount_adjust_cow(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       enum xfs_refc_adjust_op adj,
+       struct xfs_defer_ops    *dfops)
+{
+       bool                    shape_changed;
+       int                     error;
+
+       agbno += XFS_REFC_COW_START;
+
+       /*
+        * Ensure that no rcextents cross the boundary of the adjustment range.
+        */
+       error = xfs_refcount_split_extent(cur, agbno, &shape_changed);
+       if (error)
+               goto out_error;
+
+       error = xfs_refcount_split_extent(cur, agbno + aglen, &shape_changed);
+       if (error)
+               goto out_error;
+
+       /*
+        * Try to merge with the left or right extents of the range.
+        */
+       error = xfs_refcount_merge_extents(cur, &agbno, &aglen, adj,
+                       XFS_FIND_RCEXT_COW, &shape_changed);
+       if (error)
+               goto out_error;
+
+       /* Now that we've taken care of the ends, adjust the middle extents */
+       error = xfs_refcount_adjust_cow_extents(cur, agbno, aglen, adj,
+                       dfops, NULL);
+       if (error)
+               goto out_error;
+
+       return 0;
+
+out_error:
+       trace_xfs_refcount_adjust_cow_error(cur->bc_mp, cur->bc_private.a.agno,
+                       error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Record a CoW allocation in the refcount btree.
+ */
+STATIC int
+__xfs_refcount_cow_alloc(
+       struct xfs_btree_cur    *rcur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       struct xfs_defer_ops    *dfops)
+{
+       int                     error;
+
+       trace_xfs_refcount_cow_increase(rcur->bc_mp, rcur->bc_private.a.agno,
+                       agbno, aglen);
+
+       /* Add refcount btree reservation */
+       error = xfs_refcount_adjust_cow(rcur, agbno, aglen,
+                       XFS_REFCOUNT_ADJUST_COW_ALLOC, dfops);
+       if (error)
+               return error;
+
+       /* Add rmap entry */
+       if (xfs_sb_version_hasrmapbt(&rcur->bc_mp->m_sb)) {
+               error = xfs_rmap_alloc_extent(rcur->bc_mp, dfops,
+                               rcur->bc_private.a.agno,
+                               agbno, aglen, XFS_RMAP_OWN_COW);
+               if (error)
+                       return error;
+       }
+
+       return error;
+}
+
+/*
+ * Remove a CoW allocation from the refcount btree.
+ */
+STATIC int
+__xfs_refcount_cow_free(
+       struct xfs_btree_cur    *rcur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       struct xfs_defer_ops    *dfops)
+{
+       int                     error;
+
+       trace_xfs_refcount_cow_decrease(rcur->bc_mp, rcur->bc_private.a.agno,
+                       agbno, aglen);
+
+       /* Remove refcount btree reservation */
+       error = xfs_refcount_adjust_cow(rcur, agbno, aglen,
+                       XFS_REFCOUNT_ADJUST_COW_FREE, dfops);
+       if (error)
+               return error;
+
+       /* Remove rmap entry */
+       if (xfs_sb_version_hasrmapbt(&rcur->bc_mp->m_sb)) {
+               error = xfs_rmap_free_extent(rcur->bc_mp, dfops,
+                               rcur->bc_private.a.agno,
+                               agbno, aglen, XFS_RMAP_OWN_COW);
+               if (error)
+                       return error;
+       }
+
+       return error;
+}
+
+/* Record a CoW staging extent in the refcount btree. */
+int
+xfs_refcount_alloc_cow_extent(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       xfs_fsblock_t                   fsb,
+       xfs_extlen_t                    len)
+{
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_ALLOC_COW,
+                       fsb, len);
+}
+
+/* Forget a CoW staging event in the refcount btree. */
+int
+xfs_refcount_free_cow_extent(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       xfs_fsblock_t                   fsb,
+       xfs_extlen_t                    len)
+{
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_FREE_COW,
+                       fsb, len);
+}
+
+struct xfs_refcount_recovery {
+       struct list_head                rr_list;
+       struct xfs_refcount_irec        rr_rrec;
+};
+
+/* Stuff an extent on the recovery list. */
+STATIC int
+xfs_refcount_recover_extent(
+       struct xfs_btree_cur            *cur,
+       union xfs_btree_rec             *rec,
+       void                            *priv)
+{
+       struct list_head                *debris = priv;
+       struct xfs_refcount_recovery    *rr;
+
+       if (be32_to_cpu(rec->refc.rc_refcount) != 1)
+               return -EFSCORRUPTED;
+
+       rr = kmem_alloc(sizeof(struct xfs_refcount_recovery), KM_SLEEP);
+       xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec);
+       list_add_tail(&rr->rr_list, debris);
+
+       return 0;
+}
+
+/* Find and remove leftover CoW reservations. */
+int
+xfs_refcount_recover_cow_leftovers(
+       struct xfs_mount                *mp,
+       xfs_agnumber_t                  agno)
+{
+       struct xfs_trans                *tp;
+       struct xfs_btree_cur            *cur;
+       struct xfs_buf                  *agbp;
+       struct xfs_refcount_recovery    *rr, *n;
+       struct list_head                debris;
+       union xfs_btree_irec            low;
+       union xfs_btree_irec            high;
+       struct xfs_defer_ops            dfops;
+       xfs_fsblock_t                   fsb;
+       xfs_agblock_t                   agbno;
+       int                             error;
+
+       if (mp->m_sb.sb_agblocks >= XFS_REFC_COW_START)
+               return -EOPNOTSUPP;
+
+       error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+       if (error)
+               return error;
+       cur = xfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL);
+
+       /* Find all the leftover CoW staging extents. */
+       INIT_LIST_HEAD(&debris);
+       memset(&low, 0, sizeof(low));
+       memset(&high, 0, sizeof(high));
+       low.rc.rc_startblock = XFS_REFC_COW_START;
+       high.rc.rc_startblock = -1U;
+       error = xfs_btree_query_range(cur, &low, &high,
+                       xfs_refcount_recover_extent, &debris);
+       if (error)
+               goto out_cursor;
+       xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+       xfs_buf_relse(agbp);
+
+       /* Now iterate the list to free the leftovers */
+       list_for_each_entry(rr, &debris, rr_list) {
+               /* Set up transaction. */
+               error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, 0, &tp);
+               if (error)
+                       goto out_free;
+
+               trace_xfs_refcount_recover_extent(mp, agno, &rr->rr_rrec);
+
+               /* Free the orphan record */
+               xfs_defer_init(&dfops, &fsb);
+               agbno = rr->rr_rrec.rc_startblock - XFS_REFC_COW_START;
+               fsb = XFS_AGB_TO_FSB(mp, agno, agbno);
+               error = xfs_refcount_free_cow_extent(mp, &dfops, fsb,
+                               rr->rr_rrec.rc_blockcount);
+               if (error)
+                       goto out_defer;
+
+               /* Free the block. */
+               xfs_bmap_add_free(mp, &dfops, fsb,
+                               rr->rr_rrec.rc_blockcount, NULL);
+
+               error = xfs_defer_finish(&tp, &dfops, NULL);
+               if (error)
+                       goto out_defer;
+
+               error = xfs_trans_commit(tp);
+               if (error)
+                       goto out_free;
+       }
+
+out_free:
+       /* Free the leftover list */
+       list_for_each_entry_safe(rr, n, &debris, rr_list) {
+               list_del(&rr->rr_list);
+               kmem_free(rr);
+       }
+       return error;
+
+out_cursor:
+       xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+       xfs_buf_relse(agbp);
+       goto out_free;
+
+out_defer:
+       xfs_defer_cancel(&dfops);
+       xfs_trans_cancel(tp);
+       goto out_free;
+}
diff --git a/fs/xfs/libxfs/xfs_refcount.h b/fs/xfs/libxfs/xfs_refcount.h
new file mode 100644 (file)
index 0000000..098dc66
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_REFCOUNT_H__
+#define __XFS_REFCOUNT_H__
+
+extern int xfs_refcount_lookup_le(struct xfs_btree_cur *cur,
+               xfs_agblock_t bno, int *stat);
+extern int xfs_refcount_lookup_ge(struct xfs_btree_cur *cur,
+               xfs_agblock_t bno, int *stat);
+extern int xfs_refcount_get_rec(struct xfs_btree_cur *cur,
+               struct xfs_refcount_irec *irec, int *stat);
+
+enum xfs_refcount_intent_type {
+       XFS_REFCOUNT_INCREASE = 1,
+       XFS_REFCOUNT_DECREASE,
+       XFS_REFCOUNT_ALLOC_COW,
+       XFS_REFCOUNT_FREE_COW,
+};
+
+struct xfs_refcount_intent {
+       struct list_head                        ri_list;
+       enum xfs_refcount_intent_type           ri_type;
+       xfs_fsblock_t                           ri_startblock;
+       xfs_extlen_t                            ri_blockcount;
+};
+
+extern int xfs_refcount_increase_extent(struct xfs_mount *mp,
+               struct xfs_defer_ops *dfops, struct xfs_bmbt_irec *irec);
+extern int xfs_refcount_decrease_extent(struct xfs_mount *mp,
+               struct xfs_defer_ops *dfops, struct xfs_bmbt_irec *irec);
+
+extern void xfs_refcount_finish_one_cleanup(struct xfs_trans *tp,
+               struct xfs_btree_cur *rcur, int error);
+extern int xfs_refcount_finish_one(struct xfs_trans *tp,
+               struct xfs_defer_ops *dfops, enum xfs_refcount_intent_type type,
+               xfs_fsblock_t startblock, xfs_extlen_t blockcount,
+               xfs_fsblock_t *new_fsb, xfs_extlen_t *new_len,
+               struct xfs_btree_cur **pcur);
+
+extern int xfs_refcount_find_shared(struct xfs_btree_cur *cur,
+               xfs_agblock_t agbno, xfs_extlen_t aglen, xfs_agblock_t *fbno,
+               xfs_extlen_t *flen, bool find_end_of_shared);
+
+extern int xfs_refcount_alloc_cow_extent(struct xfs_mount *mp,
+               struct xfs_defer_ops *dfops, xfs_fsblock_t fsb,
+               xfs_extlen_t len);
+extern int xfs_refcount_free_cow_extent(struct xfs_mount *mp,
+               struct xfs_defer_ops *dfops, xfs_fsblock_t fsb,
+               xfs_extlen_t len);
+extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
+               xfs_agnumber_t agno);
+
+#endif /* __XFS_REFCOUNT_H__ */
diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c
new file mode 100644 (file)
index 0000000..453bb27
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_sb.h"
+#include "xfs_mount.h"
+#include "xfs_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_error.h"
+#include "xfs_trace.h"
+#include "xfs_cksum.h"
+#include "xfs_trans.h"
+#include "xfs_bit.h"
+#include "xfs_rmap.h"
+
+static struct xfs_btree_cur *
+xfs_refcountbt_dup_cursor(
+       struct xfs_btree_cur    *cur)
+{
+       return xfs_refcountbt_init_cursor(cur->bc_mp, cur->bc_tp,
+                       cur->bc_private.a.agbp, cur->bc_private.a.agno,
+                       cur->bc_private.a.dfops);
+}
+
+STATIC void
+xfs_refcountbt_set_root(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_ptr     *ptr,
+       int                     inc)
+{
+       struct xfs_buf          *agbp = cur->bc_private.a.agbp;
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(agbp);
+       xfs_agnumber_t          seqno = be32_to_cpu(agf->agf_seqno);
+       struct xfs_perag        *pag = xfs_perag_get(cur->bc_mp, seqno);
+
+       ASSERT(ptr->s != 0);
+
+       agf->agf_refcount_root = ptr->s;
+       be32_add_cpu(&agf->agf_refcount_level, inc);
+       pag->pagf_refcount_level += inc;
+       xfs_perag_put(pag);
+
+       xfs_alloc_log_agf(cur->bc_tp, agbp,
+                       XFS_AGF_REFCOUNT_ROOT | XFS_AGF_REFCOUNT_LEVEL);
+}
+
+STATIC int
+xfs_refcountbt_alloc_block(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_ptr     *start,
+       union xfs_btree_ptr     *new,
+       int                     *stat)
+{
+       struct xfs_buf          *agbp = cur->bc_private.a.agbp;
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(agbp);
+       struct xfs_alloc_arg    args;           /* block allocation args */
+       int                     error;          /* error return value */
+
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+
+       memset(&args, 0, sizeof(args));
+       args.tp = cur->bc_tp;
+       args.mp = cur->bc_mp;
+       args.type = XFS_ALLOCTYPE_NEAR_BNO;
+       args.fsbno = XFS_AGB_TO_FSB(cur->bc_mp, cur->bc_private.a.agno,
+                       xfs_refc_block(args.mp));
+       args.firstblock = args.fsbno;
+       xfs_rmap_ag_owner(&args.oinfo, XFS_RMAP_OWN_REFC);
+       args.minlen = args.maxlen = args.prod = 1;
+       args.resv = XFS_AG_RESV_METADATA;
+
+       error = xfs_alloc_vextent(&args);
+       if (error)
+               goto out_error;
+       trace_xfs_refcountbt_alloc_block(cur->bc_mp, cur->bc_private.a.agno,
+                       args.agbno, 1);
+       if (args.fsbno == NULLFSBLOCK) {
+               XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+               *stat = 0;
+               return 0;
+       }
+       ASSERT(args.agno == cur->bc_private.a.agno);
+       ASSERT(args.len == 1);
+
+       new->s = cpu_to_be32(args.agbno);
+       be32_add_cpu(&agf->agf_refcount_blocks, 1);
+       xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_REFCOUNT_BLOCKS);
+
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+       *stat = 1;
+       return 0;
+
+out_error:
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+       return error;
+}
+
+STATIC int
+xfs_refcountbt_free_block(
+       struct xfs_btree_cur    *cur,
+       struct xfs_buf          *bp)
+{
+       struct xfs_mount        *mp = cur->bc_mp;
+       struct xfs_buf          *agbp = cur->bc_private.a.agbp;
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(agbp);
+       xfs_fsblock_t           fsbno = XFS_DADDR_TO_FSB(mp, XFS_BUF_ADDR(bp));
+       struct xfs_owner_info   oinfo;
+       int                     error;
+
+       trace_xfs_refcountbt_free_block(cur->bc_mp, cur->bc_private.a.agno,
+                       XFS_FSB_TO_AGBNO(cur->bc_mp, fsbno), 1);
+       xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_REFC);
+       be32_add_cpu(&agf->agf_refcount_blocks, -1);
+       xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_REFCOUNT_BLOCKS);
+       error = xfs_free_extent(cur->bc_tp, fsbno, 1, &oinfo,
+                       XFS_AG_RESV_METADATA);
+       if (error)
+               return error;
+
+       return error;
+}
+
+STATIC int
+xfs_refcountbt_get_minrecs(
+       struct xfs_btree_cur    *cur,
+       int                     level)
+{
+       return cur->bc_mp->m_refc_mnr[level != 0];
+}
+
+STATIC int
+xfs_refcountbt_get_maxrecs(
+       struct xfs_btree_cur    *cur,
+       int                     level)
+{
+       return cur->bc_mp->m_refc_mxr[level != 0];
+}
+
+STATIC void
+xfs_refcountbt_init_key_from_rec(
+       union xfs_btree_key     *key,
+       union xfs_btree_rec     *rec)
+{
+       key->refc.rc_startblock = rec->refc.rc_startblock;
+}
+
+STATIC void
+xfs_refcountbt_init_high_key_from_rec(
+       union xfs_btree_key     *key,
+       union xfs_btree_rec     *rec)
+{
+       __u32                   x;
+
+       x = be32_to_cpu(rec->refc.rc_startblock);
+       x += be32_to_cpu(rec->refc.rc_blockcount) - 1;
+       key->refc.rc_startblock = cpu_to_be32(x);
+}
+
+STATIC void
+xfs_refcountbt_init_rec_from_cur(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_rec     *rec)
+{
+       rec->refc.rc_startblock = cpu_to_be32(cur->bc_rec.rc.rc_startblock);
+       rec->refc.rc_blockcount = cpu_to_be32(cur->bc_rec.rc.rc_blockcount);
+       rec->refc.rc_refcount = cpu_to_be32(cur->bc_rec.rc.rc_refcount);
+}
+
+STATIC void
+xfs_refcountbt_init_ptr_from_cur(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_ptr     *ptr)
+{
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+
+       ASSERT(cur->bc_private.a.agno == be32_to_cpu(agf->agf_seqno));
+       ASSERT(agf->agf_refcount_root != 0);
+
+       ptr->s = agf->agf_refcount_root;
+}
+
+STATIC __int64_t
+xfs_refcountbt_key_diff(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_key     *key)
+{
+       struct xfs_refcount_irec        *rec = &cur->bc_rec.rc;
+       struct xfs_refcount_key         *kp = &key->refc;
+
+       return (__int64_t)be32_to_cpu(kp->rc_startblock) - rec->rc_startblock;
+}
+
+STATIC __int64_t
+xfs_refcountbt_diff_two_keys(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_key     *k1,
+       union xfs_btree_key     *k2)
+{
+       return (__int64_t)be32_to_cpu(k1->refc.rc_startblock) -
+                         be32_to_cpu(k2->refc.rc_startblock);
+}
+
+STATIC bool
+xfs_refcountbt_verify(
+       struct xfs_buf          *bp)
+{
+       struct xfs_mount        *mp = bp->b_target->bt_mount;
+       struct xfs_btree_block  *block = XFS_BUF_TO_BLOCK(bp);
+       struct xfs_perag        *pag = bp->b_pag;
+       unsigned int            level;
+
+       if (block->bb_magic != cpu_to_be32(XFS_REFC_CRC_MAGIC))
+               return false;
+
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return false;
+       if (!xfs_btree_sblock_v5hdr_verify(bp))
+               return false;
+
+       level = be16_to_cpu(block->bb_level);
+       if (pag && pag->pagf_init) {
+               if (level >= pag->pagf_refcount_level)
+                       return false;
+       } else if (level >= mp->m_refc_maxlevels)
+               return false;
+
+       return xfs_btree_sblock_verify(bp, mp->m_refc_mxr[level != 0]);
+}
+
+STATIC void
+xfs_refcountbt_read_verify(
+       struct xfs_buf  *bp)
+{
+       if (!xfs_btree_sblock_verify_crc(bp))
+               xfs_buf_ioerror(bp, -EFSBADCRC);
+       else if (!xfs_refcountbt_verify(bp))
+               xfs_buf_ioerror(bp, -EFSCORRUPTED);
+
+       if (bp->b_error) {
+               trace_xfs_btree_corrupt(bp, _RET_IP_);
+               xfs_verifier_error(bp);
+       }
+}
+
+STATIC void
+xfs_refcountbt_write_verify(
+       struct xfs_buf  *bp)
+{
+       if (!xfs_refcountbt_verify(bp)) {
+               trace_xfs_btree_corrupt(bp, _RET_IP_);
+               xfs_buf_ioerror(bp, -EFSCORRUPTED);
+               xfs_verifier_error(bp);
+               return;
+       }
+       xfs_btree_sblock_calc_crc(bp);
+
+}
+
+const struct xfs_buf_ops xfs_refcountbt_buf_ops = {
+       .name                   = "xfs_refcountbt",
+       .verify_read            = xfs_refcountbt_read_verify,
+       .verify_write           = xfs_refcountbt_write_verify,
+};
+
+#if defined(DEBUG) || defined(XFS_WARN)
+STATIC int
+xfs_refcountbt_keys_inorder(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_key     *k1,
+       union xfs_btree_key     *k2)
+{
+       return be32_to_cpu(k1->refc.rc_startblock) <
+              be32_to_cpu(k2->refc.rc_startblock);
+}
+
+STATIC int
+xfs_refcountbt_recs_inorder(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_rec     *r1,
+       union xfs_btree_rec     *r2)
+{
+       return  be32_to_cpu(r1->refc.rc_startblock) +
+               be32_to_cpu(r1->refc.rc_blockcount) <=
+               be32_to_cpu(r2->refc.rc_startblock);
+}
+#endif
+
+static const struct xfs_btree_ops xfs_refcountbt_ops = {
+       .rec_len                = sizeof(struct xfs_refcount_rec),
+       .key_len                = sizeof(struct xfs_refcount_key),
+
+       .dup_cursor             = xfs_refcountbt_dup_cursor,
+       .set_root               = xfs_refcountbt_set_root,
+       .alloc_block            = xfs_refcountbt_alloc_block,
+       .free_block             = xfs_refcountbt_free_block,
+       .get_minrecs            = xfs_refcountbt_get_minrecs,
+       .get_maxrecs            = xfs_refcountbt_get_maxrecs,
+       .init_key_from_rec      = xfs_refcountbt_init_key_from_rec,
+       .init_high_key_from_rec = xfs_refcountbt_init_high_key_from_rec,
+       .init_rec_from_cur      = xfs_refcountbt_init_rec_from_cur,
+       .init_ptr_from_cur      = xfs_refcountbt_init_ptr_from_cur,
+       .key_diff               = xfs_refcountbt_key_diff,
+       .buf_ops                = &xfs_refcountbt_buf_ops,
+       .diff_two_keys          = xfs_refcountbt_diff_two_keys,
+#if defined(DEBUG) || defined(XFS_WARN)
+       .keys_inorder           = xfs_refcountbt_keys_inorder,
+       .recs_inorder           = xfs_refcountbt_recs_inorder,
+#endif
+};
+
+/*
+ * Allocate a new refcount btree cursor.
+ */
+struct xfs_btree_cur *
+xfs_refcountbt_init_cursor(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       struct xfs_buf          *agbp,
+       xfs_agnumber_t          agno,
+       struct xfs_defer_ops    *dfops)
+{
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(agbp);
+       struct xfs_btree_cur    *cur;
+
+       ASSERT(agno != NULLAGNUMBER);
+       ASSERT(agno < mp->m_sb.sb_agcount);
+       cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_NOFS);
+
+       cur->bc_tp = tp;
+       cur->bc_mp = mp;
+       cur->bc_btnum = XFS_BTNUM_REFC;
+       cur->bc_blocklog = mp->m_sb.sb_blocklog;
+       cur->bc_ops = &xfs_refcountbt_ops;
+
+       cur->bc_nlevels = be32_to_cpu(agf->agf_refcount_level);
+
+       cur->bc_private.a.agbp = agbp;
+       cur->bc_private.a.agno = agno;
+       cur->bc_private.a.dfops = dfops;
+       cur->bc_flags |= XFS_BTREE_CRC_BLOCKS;
+
+       cur->bc_private.a.priv.refc.nr_ops = 0;
+       cur->bc_private.a.priv.refc.shape_changes = 0;
+
+       return cur;
+}
+
+/*
+ * Calculate the number of records in a refcount btree block.
+ */
+int
+xfs_refcountbt_maxrecs(
+       struct xfs_mount        *mp,
+       int                     blocklen,
+       bool                    leaf)
+{
+       blocklen -= XFS_REFCOUNT_BLOCK_LEN;
+
+       if (leaf)
+               return blocklen / sizeof(struct xfs_refcount_rec);
+       return blocklen / (sizeof(struct xfs_refcount_key) +
+                          sizeof(xfs_refcount_ptr_t));
+}
+
+/* Compute the maximum height of a refcount btree. */
+void
+xfs_refcountbt_compute_maxlevels(
+       struct xfs_mount                *mp)
+{
+       mp->m_refc_maxlevels = xfs_btree_compute_maxlevels(mp,
+                       mp->m_refc_mnr, mp->m_sb.sb_agblocks);
+}
+
+/* Calculate the refcount btree size for some records. */
+xfs_extlen_t
+xfs_refcountbt_calc_size(
+       struct xfs_mount        *mp,
+       unsigned long long      len)
+{
+       return xfs_btree_calc_size(mp, mp->m_refc_mnr, len);
+}
+
+/*
+ * Calculate the maximum refcount btree size.
+ */
+xfs_extlen_t
+xfs_refcountbt_max_size(
+       struct xfs_mount        *mp)
+{
+       /* Bail out if we're uninitialized, which can happen in mkfs. */
+       if (mp->m_refc_mxr[0] == 0)
+               return 0;
+
+       return xfs_refcountbt_calc_size(mp, mp->m_sb.sb_agblocks);
+}
+
+/*
+ * Figure out how many blocks to reserve and how many are used by this btree.
+ */
+int
+xfs_refcountbt_calc_reserves(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno,
+       xfs_extlen_t            *ask,
+       xfs_extlen_t            *used)
+{
+       struct xfs_buf          *agbp;
+       struct xfs_agf          *agf;
+       xfs_extlen_t            tree_len;
+       int                     error;
+
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       *ask += xfs_refcountbt_max_size(mp);
+
+       error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+       if (error)
+               return error;
+
+       agf = XFS_BUF_TO_AGF(agbp);
+       tree_len = be32_to_cpu(agf->agf_refcount_blocks);
+       xfs_buf_relse(agbp);
+
+       *used += tree_len;
+
+       return error;
+}
diff --git a/fs/xfs/libxfs/xfs_refcount_btree.h b/fs/xfs/libxfs/xfs_refcount_btree.h
new file mode 100644 (file)
index 0000000..3be7768
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_REFCOUNT_BTREE_H__
+#define        __XFS_REFCOUNT_BTREE_H__
+
+/*
+ * Reference Count Btree on-disk structures
+ */
+
+struct xfs_buf;
+struct xfs_btree_cur;
+struct xfs_mount;
+
+/*
+ * Btree block header size
+ */
+#define XFS_REFCOUNT_BLOCK_LEN XFS_BTREE_SBLOCK_CRC_LEN
+
+/*
+ * Record, key, and pointer address macros for btree blocks.
+ *
+ * (note that some of these may appear unused, but they are used in userspace)
+ */
+#define XFS_REFCOUNT_REC_ADDR(block, index) \
+       ((struct xfs_refcount_rec *) \
+               ((char *)(block) + \
+                XFS_REFCOUNT_BLOCK_LEN + \
+                (((index) - 1) * sizeof(struct xfs_refcount_rec))))
+
+#define XFS_REFCOUNT_KEY_ADDR(block, index) \
+       ((struct xfs_refcount_key *) \
+               ((char *)(block) + \
+                XFS_REFCOUNT_BLOCK_LEN + \
+                ((index) - 1) * sizeof(struct xfs_refcount_key)))
+
+#define XFS_REFCOUNT_PTR_ADDR(block, index, maxrecs) \
+       ((xfs_refcount_ptr_t *) \
+               ((char *)(block) + \
+                XFS_REFCOUNT_BLOCK_LEN + \
+                (maxrecs) * sizeof(struct xfs_refcount_key) + \
+                ((index) - 1) * sizeof(xfs_refcount_ptr_t)))
+
+extern struct xfs_btree_cur *xfs_refcountbt_init_cursor(struct xfs_mount *mp,
+               struct xfs_trans *tp, struct xfs_buf *agbp, xfs_agnumber_t agno,
+               struct xfs_defer_ops *dfops);
+extern int xfs_refcountbt_maxrecs(struct xfs_mount *mp, int blocklen,
+               bool leaf);
+extern void xfs_refcountbt_compute_maxlevels(struct xfs_mount *mp);
+
+extern xfs_extlen_t xfs_refcountbt_calc_size(struct xfs_mount *mp,
+               unsigned long long len);
+extern xfs_extlen_t xfs_refcountbt_max_size(struct xfs_mount *mp);
+
+extern int xfs_refcountbt_calc_reserves(struct xfs_mount *mp,
+               xfs_agnumber_t agno, xfs_extlen_t *ask, xfs_extlen_t *used);
+
+#endif /* __XFS_REFCOUNT_BTREE_H__ */
index 73d05407d6636240a4c9ef29442102bbb9d9a198..3a8cc7139912bf83d0830090c3c88a91e79b6677 100644 (file)
@@ -148,6 +148,37 @@ done:
        return error;
 }
 
+STATIC int
+xfs_rmap_delete(
+       struct xfs_btree_cur    *rcur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            len,
+       uint64_t                owner,
+       uint64_t                offset,
+       unsigned int            flags)
+{
+       int                     i;
+       int                     error;
+
+       trace_xfs_rmap_delete(rcur->bc_mp, rcur->bc_private.a.agno, agbno,
+                       len, owner, offset, flags);
+
+       error = xfs_rmap_lookup_eq(rcur, agbno, len, owner, offset, flags, &i);
+       if (error)
+               goto done;
+       XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
+
+       error = xfs_btree_delete(rcur, &i);
+       if (error)
+               goto done;
+       XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
+done:
+       if (error)
+               trace_xfs_rmap_delete_error(rcur->bc_mp,
+                               rcur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
 static int
 xfs_rmap_btrec_to_irec(
        union xfs_btree_rec     *rec,
@@ -180,6 +211,160 @@ xfs_rmap_get_rec(
        return xfs_rmap_btrec_to_irec(rec, irec);
 }
 
+struct xfs_find_left_neighbor_info {
+       struct xfs_rmap_irec    high;
+       struct xfs_rmap_irec    *irec;
+       int                     *stat;
+};
+
+/* For each rmap given, figure out if it matches the key we want. */
+STATIC int
+xfs_rmap_find_left_neighbor_helper(
+       struct xfs_btree_cur    *cur,
+       struct xfs_rmap_irec    *rec,
+       void                    *priv)
+{
+       struct xfs_find_left_neighbor_info      *info = priv;
+
+       trace_xfs_rmap_find_left_neighbor_candidate(cur->bc_mp,
+                       cur->bc_private.a.agno, rec->rm_startblock,
+                       rec->rm_blockcount, rec->rm_owner, rec->rm_offset,
+                       rec->rm_flags);
+
+       if (rec->rm_owner != info->high.rm_owner)
+               return XFS_BTREE_QUERY_RANGE_CONTINUE;
+       if (!XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) &&
+           !(rec->rm_flags & XFS_RMAP_BMBT_BLOCK) &&
+           rec->rm_offset + rec->rm_blockcount - 1 != info->high.rm_offset)
+               return XFS_BTREE_QUERY_RANGE_CONTINUE;
+
+       *info->irec = *rec;
+       *info->stat = 1;
+       return XFS_BTREE_QUERY_RANGE_ABORT;
+}
+
+/*
+ * Find the record to the left of the given extent, being careful only to
+ * return a match with the same owner and adjacent physical and logical
+ * block ranges.
+ */
+int
+xfs_rmap_find_left_neighbor(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       uint64_t                owner,
+       uint64_t                offset,
+       unsigned int            flags,
+       struct xfs_rmap_irec    *irec,
+       int                     *stat)
+{
+       struct xfs_find_left_neighbor_info      info;
+       int                     error;
+
+       *stat = 0;
+       if (bno == 0)
+               return 0;
+       info.high.rm_startblock = bno - 1;
+       info.high.rm_owner = owner;
+       if (!XFS_RMAP_NON_INODE_OWNER(owner) &&
+           !(flags & XFS_RMAP_BMBT_BLOCK)) {
+               if (offset == 0)
+                       return 0;
+               info.high.rm_offset = offset - 1;
+       } else
+               info.high.rm_offset = 0;
+       info.high.rm_flags = flags;
+       info.high.rm_blockcount = 0;
+       info.irec = irec;
+       info.stat = stat;
+
+       trace_xfs_rmap_find_left_neighbor_query(cur->bc_mp,
+                       cur->bc_private.a.agno, bno, 0, owner, offset, flags);
+
+       error = xfs_rmap_query_range(cur, &info.high, &info.high,
+                       xfs_rmap_find_left_neighbor_helper, &info);
+       if (error == XFS_BTREE_QUERY_RANGE_ABORT)
+               error = 0;
+       if (*stat)
+               trace_xfs_rmap_find_left_neighbor_result(cur->bc_mp,
+                               cur->bc_private.a.agno, irec->rm_startblock,
+                               irec->rm_blockcount, irec->rm_owner,
+                               irec->rm_offset, irec->rm_flags);
+       return error;
+}
+
+/* For each rmap given, figure out if it matches the key we want. */
+STATIC int
+xfs_rmap_lookup_le_range_helper(
+       struct xfs_btree_cur    *cur,
+       struct xfs_rmap_irec    *rec,
+       void                    *priv)
+{
+       struct xfs_find_left_neighbor_info      *info = priv;
+
+       trace_xfs_rmap_lookup_le_range_candidate(cur->bc_mp,
+                       cur->bc_private.a.agno, rec->rm_startblock,
+                       rec->rm_blockcount, rec->rm_owner, rec->rm_offset,
+                       rec->rm_flags);
+
+       if (rec->rm_owner != info->high.rm_owner)
+               return XFS_BTREE_QUERY_RANGE_CONTINUE;
+       if (!XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) &&
+           !(rec->rm_flags & XFS_RMAP_BMBT_BLOCK) &&
+           (rec->rm_offset > info->high.rm_offset ||
+            rec->rm_offset + rec->rm_blockcount <= info->high.rm_offset))
+               return XFS_BTREE_QUERY_RANGE_CONTINUE;
+
+       *info->irec = *rec;
+       *info->stat = 1;
+       return XFS_BTREE_QUERY_RANGE_ABORT;
+}
+
+/*
+ * Find the record to the left of the given extent, being careful only to
+ * return a match with the same owner and overlapping physical and logical
+ * block ranges.  This is the overlapping-interval version of
+ * xfs_rmap_lookup_le.
+ */
+int
+xfs_rmap_lookup_le_range(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       uint64_t                owner,
+       uint64_t                offset,
+       unsigned int            flags,
+       struct xfs_rmap_irec    *irec,
+       int                     *stat)
+{
+       struct xfs_find_left_neighbor_info      info;
+       int                     error;
+
+       info.high.rm_startblock = bno;
+       info.high.rm_owner = owner;
+       if (!XFS_RMAP_NON_INODE_OWNER(owner) && !(flags & XFS_RMAP_BMBT_BLOCK))
+               info.high.rm_offset = offset;
+       else
+               info.high.rm_offset = 0;
+       info.high.rm_flags = flags;
+       info.high.rm_blockcount = 0;
+       *stat = 0;
+       info.irec = irec;
+       info.stat = stat;
+
+       trace_xfs_rmap_lookup_le_range(cur->bc_mp,
+                       cur->bc_private.a.agno, bno, 0, owner, offset, flags);
+       error = xfs_rmap_query_range(cur, &info.high, &info.high,
+                       xfs_rmap_lookup_le_range_helper, &info);
+       if (error == XFS_BTREE_QUERY_RANGE_ABORT)
+               error = 0;
+       if (*stat)
+               trace_xfs_rmap_lookup_le_range_result(cur->bc_mp,
+                               cur->bc_private.a.agno, irec->rm_startblock,
+                               irec->rm_blockcount, irec->rm_owner,
+                               irec->rm_offset, irec->rm_flags);
+       return error;
+}
+
 /*
  * Find the extent in the rmap btree and remove it.
  *
@@ -1093,123 +1278,816 @@ done:
        return error;
 }
 
-#undef NEW
-#undef LEFT
-#undef RIGHT
-#undef PREV
-
-struct xfs_rmap_query_range_info {
-       xfs_rmap_query_range_fn fn;
-       void                            *priv;
-};
-
-/* Format btree record and pass to our callback. */
+/*
+ * Convert an unwritten extent to a real extent or vice versa.  If there is no
+ * possibility of overlapping extents, delegate to the simpler convert
+ * function.
+ */
 STATIC int
-xfs_rmap_query_range_helper(
+xfs_rmap_convert_shared(
        struct xfs_btree_cur    *cur,
-       union xfs_btree_rec     *rec,
-       void                    *priv)
+       xfs_agblock_t           bno,
+       xfs_extlen_t            len,
+       bool                    unwritten,
+       struct xfs_owner_info   *oinfo)
 {
-       struct xfs_rmap_query_range_info        *query = priv;
-       struct xfs_rmap_irec                    irec;
-       int                                     error;
+       struct xfs_mount        *mp = cur->bc_mp;
+       struct xfs_rmap_irec    r[4];   /* neighbor extent entries */
+                                       /* left is 0, right is 1, prev is 2 */
+                                       /* new is 3 */
+       uint64_t                owner;
+       uint64_t                offset;
+       uint64_t                new_endoff;
+       unsigned int            oldext;
+       unsigned int            newext;
+       unsigned int            flags = 0;
+       int                     i;
+       int                     state = 0;
+       int                     error;
 
-       error = xfs_rmap_btrec_to_irec(rec, &irec);
-       if (error)
-               return error;
-       return query->fn(cur, &irec, query->priv);
-}
+       xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
+       ASSERT(!(XFS_RMAP_NON_INODE_OWNER(owner) ||
+                       (flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))));
+       oldext = unwritten ? XFS_RMAP_UNWRITTEN : 0;
+       new_endoff = offset + len;
+       trace_xfs_rmap_convert(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
 
-/* Find all rmaps between two keys. */
-int
-xfs_rmap_query_range(
-       struct xfs_btree_cur            *cur,
-       struct xfs_rmap_irec            *low_rec,
-       struct xfs_rmap_irec            *high_rec,
-       xfs_rmap_query_range_fn fn,
-       void                            *priv)
-{
-       union xfs_btree_irec            low_brec;
-       union xfs_btree_irec            high_brec;
-       struct xfs_rmap_query_range_info        query;
+       /*
+        * For the initial lookup, look for and exact match or the left-adjacent
+        * record for our insertion point. This will also give us the record for
+        * start block contiguity tests.
+        */
+       error = xfs_rmap_lookup_le_range(cur, bno, owner, offset, flags,
+                       &PREV, &i);
+       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
 
-       low_brec.r = *low_rec;
-       high_brec.r = *high_rec;
-       query.priv = priv;
-       query.fn = fn;
-       return xfs_btree_query_range(cur, &low_brec, &high_brec,
-                       xfs_rmap_query_range_helper, &query);
-}
+       ASSERT(PREV.rm_offset <= offset);
+       ASSERT(PREV.rm_offset + PREV.rm_blockcount >= new_endoff);
+       ASSERT((PREV.rm_flags & XFS_RMAP_UNWRITTEN) == oldext);
+       newext = ~oldext & XFS_RMAP_UNWRITTEN;
 
-/* Clean up after calling xfs_rmap_finish_one. */
-void
-xfs_rmap_finish_one_cleanup(
-       struct xfs_trans        *tp,
-       struct xfs_btree_cur    *rcur,
-       int                     error)
-{
-       struct xfs_buf          *agbp;
+       /*
+        * Set flags determining what part of the previous oldext allocation
+        * extent is being replaced by a newext allocation.
+        */
+       if (PREV.rm_offset == offset)
+               state |= RMAP_LEFT_FILLING;
+       if (PREV.rm_offset + PREV.rm_blockcount == new_endoff)
+               state |= RMAP_RIGHT_FILLING;
 
-       if (rcur == NULL)
-               return;
-       agbp = rcur->bc_private.a.agbp;
-       xfs_btree_del_cursor(rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+       /* Is there a left record that abuts our range? */
+       error = xfs_rmap_find_left_neighbor(cur, bno, owner, offset, newext,
+                       &LEFT, &i);
        if (error)
-               xfs_trans_brelse(tp, agbp);
-}
-
-/*
- * Process one of the deferred rmap operations.  We pass back the
- * btree cursor to maintain our lock on the rmapbt between calls.
- * This saves time and eliminates a buffer deadlock between the
- * superblock and the AGF because we'll always grab them in the same
- * order.
- */
-int
-xfs_rmap_finish_one(
-       struct xfs_trans                *tp,
-       enum xfs_rmap_intent_type       type,
-       __uint64_t                      owner,
-       int                             whichfork,
-       xfs_fileoff_t                   startoff,
-       xfs_fsblock_t                   startblock,
-       xfs_filblks_t                   blockcount,
-       xfs_exntst_t                    state,
-       struct xfs_btree_cur            **pcur)
-{
-       struct xfs_mount                *mp = tp->t_mountp;
-       struct xfs_btree_cur            *rcur;
-       struct xfs_buf                  *agbp = NULL;
-       int                             error = 0;
-       xfs_agnumber_t                  agno;
-       struct xfs_owner_info           oinfo;
-       xfs_agblock_t                   bno;
-       bool                            unwritten;
-
-       agno = XFS_FSB_TO_AGNO(mp, startblock);
-       ASSERT(agno != NULLAGNUMBER);
-       bno = XFS_FSB_TO_AGBNO(mp, startblock);
+               goto done;
+       if (i) {
+               state |= RMAP_LEFT_VALID;
+               XFS_WANT_CORRUPTED_GOTO(mp,
+                               LEFT.rm_startblock + LEFT.rm_blockcount <= bno,
+                               done);
+               if (xfs_rmap_is_mergeable(&LEFT, owner, newext))
+                       state |= RMAP_LEFT_CONTIG;
+       }
 
-       trace_xfs_rmap_deferred(mp, agno, type, bno, owner, whichfork,
-                       startoff, blockcount, state);
+       /* Is there a right record that abuts our range? */
+       error = xfs_rmap_lookup_eq(cur, bno + len, len, owner, offset + len,
+                       newext, &i);
+       if (error)
+               goto done;
+       if (i) {
+               state |= RMAP_RIGHT_VALID;
+               error = xfs_rmap_get_rec(cur, &RIGHT, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               XFS_WANT_CORRUPTED_GOTO(mp, bno + len <= RIGHT.rm_startblock,
+                               done);
+               trace_xfs_rmap_find_right_neighbor_result(cur->bc_mp,
+                               cur->bc_private.a.agno, RIGHT.rm_startblock,
+                               RIGHT.rm_blockcount, RIGHT.rm_owner,
+                               RIGHT.rm_offset, RIGHT.rm_flags);
+               if (xfs_rmap_is_mergeable(&RIGHT, owner, newext))
+                       state |= RMAP_RIGHT_CONTIG;
+       }
 
-       if (XFS_TEST_ERROR(false, mp,
-                       XFS_ERRTAG_RMAP_FINISH_ONE,
-                       XFS_RANDOM_RMAP_FINISH_ONE))
-               return -EIO;
+       /* check that left + prev + right is not too long */
+       if ((state & (RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
+                        RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG)) ==
+           (RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
+            RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG) &&
+           (unsigned long)LEFT.rm_blockcount + len +
+            RIGHT.rm_blockcount > XFS_RMAP_LEN_MAX)
+               state &= ~RMAP_RIGHT_CONTIG;
 
+       trace_xfs_rmap_convert_state(mp, cur->bc_private.a.agno, state,
+                       _RET_IP_);
        /*
-        * If we haven't gotten a cursor or the cursor AG doesn't match
-        * the startblock, get one now.
+        * Switch out based on the FILLING and CONTIG state bits.
         */
-       rcur = *pcur;
-       if (rcur != NULL && rcur->bc_private.a.agno != agno) {
-               xfs_rmap_finish_one_cleanup(tp, rcur, 0);
-               rcur = NULL;
-               *pcur = NULL;
-       }
-       if (rcur == NULL) {
-               /*
+       switch (state & (RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
+                        RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG)) {
+       case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
+            RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
+               /*
+                * Setting all of a previous oldext extent to newext.
+                * The left and right neighbors are both contiguous with new.
+                */
+               error = xfs_rmap_delete(cur, RIGHT.rm_startblock,
+                               RIGHT.rm_blockcount, RIGHT.rm_owner,
+                               RIGHT.rm_offset, RIGHT.rm_flags);
+               if (error)
+                       goto done;
+               error = xfs_rmap_delete(cur, PREV.rm_startblock,
+                               PREV.rm_blockcount, PREV.rm_owner,
+                               PREV.rm_offset, PREV.rm_flags);
+               if (error)
+                       goto done;
+               NEW = LEFT;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount += PREV.rm_blockcount + RIGHT.rm_blockcount;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG:
+               /*
+                * Setting all of a previous oldext extent to newext.
+                * The left neighbor is contiguous, the right is not.
+                */
+               error = xfs_rmap_delete(cur, PREV.rm_startblock,
+                               PREV.rm_blockcount, PREV.rm_owner,
+                               PREV.rm_offset, PREV.rm_flags);
+               if (error)
+                       goto done;
+               NEW = LEFT;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount += PREV.rm_blockcount;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
+               /*
+                * Setting all of a previous oldext extent to newext.
+                * The right neighbor is contiguous, the left is not.
+                */
+               error = xfs_rmap_delete(cur, RIGHT.rm_startblock,
+                               RIGHT.rm_blockcount, RIGHT.rm_owner,
+                               RIGHT.rm_offset, RIGHT.rm_flags);
+               if (error)
+                       goto done;
+               NEW = PREV;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount += RIGHT.rm_blockcount;
+               NEW.rm_flags = RIGHT.rm_flags;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING:
+               /*
+                * Setting all of a previous oldext extent to newext.
+                * Neither the left nor right neighbors are contiguous with
+                * the new one.
+                */
+               NEW = PREV;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_flags = newext;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG:
+               /*
+                * Setting the first part of a previous oldext extent to newext.
+                * The left neighbor is contiguous.
+                */
+               NEW = PREV;
+               error = xfs_rmap_delete(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               NEW.rm_offset += len;
+               NEW.rm_startblock += len;
+               NEW.rm_blockcount -= len;
+               error = xfs_rmap_insert(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               NEW = LEFT;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount += len;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING:
+               /*
+                * Setting the first part of a previous oldext extent to newext.
+                * The left neighbor is not contiguous.
+                */
+               NEW = PREV;
+               error = xfs_rmap_delete(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               NEW.rm_offset += len;
+               NEW.rm_startblock += len;
+               NEW.rm_blockcount -= len;
+               error = xfs_rmap_insert(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               error = xfs_rmap_insert(cur, bno, len, owner, offset, newext);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
+               /*
+                * Setting the last part of a previous oldext extent to newext.
+                * The right neighbor is contiguous with the new allocation.
+                */
+               NEW = PREV;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount = offset - NEW.rm_offset;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               NEW = RIGHT;
+               error = xfs_rmap_delete(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               NEW.rm_offset = offset;
+               NEW.rm_startblock = bno;
+               NEW.rm_blockcount += len;
+               error = xfs_rmap_insert(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_RIGHT_FILLING:
+               /*
+                * Setting the last part of a previous oldext extent to newext.
+                * The right neighbor is not contiguous.
+                */
+               NEW = PREV;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount -= len;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               error = xfs_rmap_insert(cur, bno, len, owner, offset, newext);
+               if (error)
+                       goto done;
+               break;
+
+       case 0:
+               /*
+                * Setting the middle part of a previous oldext extent to
+                * newext.  Contiguity is impossible here.
+                * One extent becomes three extents.
+                */
+               /* new right extent - oldext */
+               NEW.rm_startblock = bno + len;
+               NEW.rm_owner = owner;
+               NEW.rm_offset = new_endoff;
+               NEW.rm_blockcount = PREV.rm_offset + PREV.rm_blockcount -
+                               new_endoff;
+               NEW.rm_flags = PREV.rm_flags;
+               error = xfs_rmap_insert(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner, NEW.rm_offset,
+                               NEW.rm_flags);
+               if (error)
+                       goto done;
+               /* new left extent - oldext */
+               NEW = PREV;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount = offset - NEW.rm_offset;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               /* new middle extent - newext */
+               NEW.rm_startblock = bno;
+               NEW.rm_blockcount = len;
+               NEW.rm_owner = owner;
+               NEW.rm_offset = offset;
+               NEW.rm_flags = newext;
+               error = xfs_rmap_insert(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner, NEW.rm_offset,
+                               NEW.rm_flags);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
+       case RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
+       case RMAP_LEFT_FILLING | RMAP_RIGHT_CONTIG:
+       case RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG:
+       case RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
+       case RMAP_LEFT_CONTIG:
+       case RMAP_RIGHT_CONTIG:
+               /*
+                * These cases are all impossible.
+                */
+               ASSERT(0);
+       }
+
+       trace_xfs_rmap_convert_done(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
+done:
+       if (error)
+               trace_xfs_rmap_convert_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+#undef NEW
+#undef LEFT
+#undef RIGHT
+#undef PREV
+
+/*
+ * Find an extent in the rmap btree and unmap it.  For rmap extent types that
+ * can overlap (data fork rmaps on reflink filesystems) we must be careful
+ * that the prev/next records in the btree might belong to another owner.
+ * Therefore we must use delete+insert to alter any of the key fields.
+ *
+ * For every other situation there can only be one owner for a given extent,
+ * so we can call the regular _free function.
+ */
+STATIC int
+xfs_rmap_unmap_shared(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       xfs_extlen_t            len,
+       bool                    unwritten,
+       struct xfs_owner_info   *oinfo)
+{
+       struct xfs_mount        *mp = cur->bc_mp;
+       struct xfs_rmap_irec    ltrec;
+       uint64_t                ltoff;
+       int                     error = 0;
+       int                     i;
+       uint64_t                owner;
+       uint64_t                offset;
+       unsigned int            flags;
+
+       xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
+       if (unwritten)
+               flags |= XFS_RMAP_UNWRITTEN;
+       trace_xfs_rmap_unmap(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
+
+       /*
+        * We should always have a left record because there's a static record
+        * for the AG headers at rm_startblock == 0 created by mkfs/growfs that
+        * will not ever be removed from the tree.
+        */
+       error = xfs_rmap_lookup_le_range(cur, bno, owner, offset, flags,
+                       &ltrec, &i);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
+       ltoff = ltrec.rm_offset;
+
+       /* Make sure the extent we found covers the entire freeing range. */
+       XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_startblock <= bno &&
+               ltrec.rm_startblock + ltrec.rm_blockcount >=
+               bno + len, out_error);
+
+       /* Make sure the owner matches what we expect to find in the tree. */
+       XFS_WANT_CORRUPTED_GOTO(mp, owner == ltrec.rm_owner, out_error);
+
+       /* Make sure the unwritten flag matches. */
+       XFS_WANT_CORRUPTED_GOTO(mp, (flags & XFS_RMAP_UNWRITTEN) ==
+                       (ltrec.rm_flags & XFS_RMAP_UNWRITTEN), out_error);
+
+       /* Check the offset. */
+       XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_offset <= offset, out_error);
+       XFS_WANT_CORRUPTED_GOTO(mp, offset <= ltoff + ltrec.rm_blockcount,
+                       out_error);
+
+       if (ltrec.rm_startblock == bno && ltrec.rm_blockcount == len) {
+               /* Exact match, simply remove the record from rmap tree. */
+               error = xfs_rmap_delete(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags);
+               if (error)
+                       goto out_error;
+       } else if (ltrec.rm_startblock == bno) {
+               /*
+                * Overlap left hand side of extent: move the start, trim the
+                * length and update the current record.
+                *
+                *       ltbno                ltlen
+                * Orig:    |oooooooooooooooooooo|
+                * Freeing: |fffffffff|
+                * Result:            |rrrrrrrrrr|
+                *         bno       len
+                */
+
+               /* Delete prev rmap. */
+               error = xfs_rmap_delete(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags);
+               if (error)
+                       goto out_error;
+
+               /* Add an rmap at the new offset. */
+               ltrec.rm_startblock += len;
+               ltrec.rm_blockcount -= len;
+               ltrec.rm_offset += len;
+               error = xfs_rmap_insert(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags);
+               if (error)
+                       goto out_error;
+       } else if (ltrec.rm_startblock + ltrec.rm_blockcount == bno + len) {
+               /*
+                * Overlap right hand side of extent: trim the length and
+                * update the current record.
+                *
+                *       ltbno                ltlen
+                * Orig:    |oooooooooooooooooooo|
+                * Freeing:            |fffffffff|
+                * Result:  |rrrrrrrrrr|
+                *                    bno       len
+                */
+               error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags, &i);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
+               ltrec.rm_blockcount -= len;
+               error = xfs_rmap_update(cur, &ltrec);
+               if (error)
+                       goto out_error;
+       } else {
+               /*
+                * Overlap middle of extent: trim the length of the existing
+                * record to the length of the new left-extent size, increment
+                * the insertion position so we can insert a new record
+                * containing the remaining right-extent space.
+                *
+                *       ltbno                ltlen
+                * Orig:    |oooooooooooooooooooo|
+                * Freeing:       |fffffffff|
+                * Result:  |rrrrr|         |rrrr|
+                *               bno       len
+                */
+               xfs_extlen_t    orig_len = ltrec.rm_blockcount;
+
+               /* Shrink the left side of the rmap */
+               error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags, &i);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
+               ltrec.rm_blockcount = bno - ltrec.rm_startblock;
+               error = xfs_rmap_update(cur, &ltrec);
+               if (error)
+                       goto out_error;
+
+               /* Add an rmap at the new offset */
+               error = xfs_rmap_insert(cur, bno + len,
+                               orig_len - len - ltrec.rm_blockcount,
+                               ltrec.rm_owner, offset + len,
+                               ltrec.rm_flags);
+               if (error)
+                       goto out_error;
+       }
+
+       trace_xfs_rmap_unmap_done(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
+out_error:
+       if (error)
+               trace_xfs_rmap_unmap_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Find an extent in the rmap btree and map it.  For rmap extent types that
+ * can overlap (data fork rmaps on reflink filesystems) we must be careful
+ * that the prev/next records in the btree might belong to another owner.
+ * Therefore we must use delete+insert to alter any of the key fields.
+ *
+ * For every other situation there can only be one owner for a given extent,
+ * so we can call the regular _alloc function.
+ */
+STATIC int
+xfs_rmap_map_shared(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       xfs_extlen_t            len,
+       bool                    unwritten,
+       struct xfs_owner_info   *oinfo)
+{
+       struct xfs_mount        *mp = cur->bc_mp;
+       struct xfs_rmap_irec    ltrec;
+       struct xfs_rmap_irec    gtrec;
+       int                     have_gt;
+       int                     have_lt;
+       int                     error = 0;
+       int                     i;
+       uint64_t                owner;
+       uint64_t                offset;
+       unsigned int            flags = 0;
+
+       xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
+       if (unwritten)
+               flags |= XFS_RMAP_UNWRITTEN;
+       trace_xfs_rmap_map(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
+
+       /* Is there a left record that abuts our range? */
+       error = xfs_rmap_find_left_neighbor(cur, bno, owner, offset, flags,
+                       &ltrec, &have_lt);
+       if (error)
+               goto out_error;
+       if (have_lt &&
+           !xfs_rmap_is_mergeable(&ltrec, owner, flags))
+               have_lt = 0;
+
+       /* Is there a right record that abuts our range? */
+       error = xfs_rmap_lookup_eq(cur, bno + len, len, owner, offset + len,
+                       flags, &have_gt);
+       if (error)
+               goto out_error;
+       if (have_gt) {
+               error = xfs_rmap_get_rec(cur, &gtrec, &have_gt);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(mp, have_gt == 1, out_error);
+               trace_xfs_rmap_find_right_neighbor_result(cur->bc_mp,
+                       cur->bc_private.a.agno, gtrec.rm_startblock,
+                       gtrec.rm_blockcount, gtrec.rm_owner,
+                       gtrec.rm_offset, gtrec.rm_flags);
+
+               if (!xfs_rmap_is_mergeable(&gtrec, owner, flags))
+                       have_gt = 0;
+       }
+
+       if (have_lt &&
+           ltrec.rm_startblock + ltrec.rm_blockcount == bno &&
+           ltrec.rm_offset + ltrec.rm_blockcount == offset) {
+               /*
+                * Left edge contiguous, merge into left record.
+                *
+                *       ltbno     ltlen
+                * orig:   |ooooooooo|
+                * adding:           |aaaaaaaaa|
+                * result: |rrrrrrrrrrrrrrrrrrr|
+                *                  bno       len
+                */
+               ltrec.rm_blockcount += len;
+               if (have_gt &&
+                   bno + len == gtrec.rm_startblock &&
+                   offset + len == gtrec.rm_offset) {
+                       /*
+                        * Right edge also contiguous, delete right record
+                        * and merge into left record.
+                        *
+                        *       ltbno     ltlen    gtbno     gtlen
+                        * orig:   |ooooooooo|         |ooooooooo|
+                        * adding:           |aaaaaaaaa|
+                        * result: |rrrrrrrrrrrrrrrrrrrrrrrrrrrrr|
+                        */
+                       ltrec.rm_blockcount += gtrec.rm_blockcount;
+                       error = xfs_rmap_delete(cur, gtrec.rm_startblock,
+                                       gtrec.rm_blockcount, gtrec.rm_owner,
+                                       gtrec.rm_offset, gtrec.rm_flags);
+                       if (error)
+                               goto out_error;
+               }
+
+               /* Point the cursor back to the left record and update. */
+               error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags, &i);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
+
+               error = xfs_rmap_update(cur, &ltrec);
+               if (error)
+                       goto out_error;
+       } else if (have_gt &&
+                  bno + len == gtrec.rm_startblock &&
+                  offset + len == gtrec.rm_offset) {
+               /*
+                * Right edge contiguous, merge into right record.
+                *
+                *                 gtbno     gtlen
+                * Orig:             |ooooooooo|
+                * adding: |aaaaaaaaa|
+                * Result: |rrrrrrrrrrrrrrrrrrr|
+                *        bno       len
+                */
+               /* Delete the old record. */
+               error = xfs_rmap_delete(cur, gtrec.rm_startblock,
+                               gtrec.rm_blockcount, gtrec.rm_owner,
+                               gtrec.rm_offset, gtrec.rm_flags);
+               if (error)
+                       goto out_error;
+
+               /* Move the start and re-add it. */
+               gtrec.rm_startblock = bno;
+               gtrec.rm_blockcount += len;
+               gtrec.rm_offset = offset;
+               error = xfs_rmap_insert(cur, gtrec.rm_startblock,
+                               gtrec.rm_blockcount, gtrec.rm_owner,
+                               gtrec.rm_offset, gtrec.rm_flags);
+               if (error)
+                       goto out_error;
+       } else {
+               /*
+                * No contiguous edge with identical owner, insert
+                * new record at current cursor position.
+                */
+               error = xfs_rmap_insert(cur, bno, len, owner, offset, flags);
+               if (error)
+                       goto out_error;
+       }
+
+       trace_xfs_rmap_map_done(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
+out_error:
+       if (error)
+               trace_xfs_rmap_map_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+struct xfs_rmap_query_range_info {
+       xfs_rmap_query_range_fn fn;
+       void                            *priv;
+};
+
+/* Format btree record and pass to our callback. */
+STATIC int
+xfs_rmap_query_range_helper(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_rec     *rec,
+       void                    *priv)
+{
+       struct xfs_rmap_query_range_info        *query = priv;
+       struct xfs_rmap_irec                    irec;
+       int                                     error;
+
+       error = xfs_rmap_btrec_to_irec(rec, &irec);
+       if (error)
+               return error;
+       return query->fn(cur, &irec, query->priv);
+}
+
+/* Find all rmaps between two keys. */
+int
+xfs_rmap_query_range(
+       struct xfs_btree_cur            *cur,
+       struct xfs_rmap_irec            *low_rec,
+       struct xfs_rmap_irec            *high_rec,
+       xfs_rmap_query_range_fn fn,
+       void                            *priv)
+{
+       union xfs_btree_irec            low_brec;
+       union xfs_btree_irec            high_brec;
+       struct xfs_rmap_query_range_info        query;
+
+       low_brec.r = *low_rec;
+       high_brec.r = *high_rec;
+       query.priv = priv;
+       query.fn = fn;
+       return xfs_btree_query_range(cur, &low_brec, &high_brec,
+                       xfs_rmap_query_range_helper, &query);
+}
+
+/* Clean up after calling xfs_rmap_finish_one. */
+void
+xfs_rmap_finish_one_cleanup(
+       struct xfs_trans        *tp,
+       struct xfs_btree_cur    *rcur,
+       int                     error)
+{
+       struct xfs_buf          *agbp;
+
+       if (rcur == NULL)
+               return;
+       agbp = rcur->bc_private.a.agbp;
+       xfs_btree_del_cursor(rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+       if (error)
+               xfs_trans_brelse(tp, agbp);
+}
+
+/*
+ * Process one of the deferred rmap operations.  We pass back the
+ * btree cursor to maintain our lock on the rmapbt between calls.
+ * This saves time and eliminates a buffer deadlock between the
+ * superblock and the AGF because we'll always grab them in the same
+ * order.
+ */
+int
+xfs_rmap_finish_one(
+       struct xfs_trans                *tp,
+       enum xfs_rmap_intent_type       type,
+       __uint64_t                      owner,
+       int                             whichfork,
+       xfs_fileoff_t                   startoff,
+       xfs_fsblock_t                   startblock,
+       xfs_filblks_t                   blockcount,
+       xfs_exntst_t                    state,
+       struct xfs_btree_cur            **pcur)
+{
+       struct xfs_mount                *mp = tp->t_mountp;
+       struct xfs_btree_cur            *rcur;
+       struct xfs_buf                  *agbp = NULL;
+       int                             error = 0;
+       xfs_agnumber_t                  agno;
+       struct xfs_owner_info           oinfo;
+       xfs_agblock_t                   bno;
+       bool                            unwritten;
+
+       agno = XFS_FSB_TO_AGNO(mp, startblock);
+       ASSERT(agno != NULLAGNUMBER);
+       bno = XFS_FSB_TO_AGBNO(mp, startblock);
+
+       trace_xfs_rmap_deferred(mp, agno, type, bno, owner, whichfork,
+                       startoff, blockcount, state);
+
+       if (XFS_TEST_ERROR(false, mp,
+                       XFS_ERRTAG_RMAP_FINISH_ONE,
+                       XFS_RANDOM_RMAP_FINISH_ONE))
+               return -EIO;
+
+       /*
+        * If we haven't gotten a cursor or the cursor AG doesn't match
+        * the startblock, get one now.
+        */
+       rcur = *pcur;
+       if (rcur != NULL && rcur->bc_private.a.agno != agno) {
+               xfs_rmap_finish_one_cleanup(tp, rcur, 0);
+               rcur = NULL;
+               *pcur = NULL;
+       }
+       if (rcur == NULL) {
+               /*
                 * Refresh the freelist before we start changing the
                 * rmapbt, because a shape change could cause us to
                 * allocate blocks.
@@ -1237,15 +2115,27 @@ xfs_rmap_finish_one(
        case XFS_RMAP_MAP:
                error = xfs_rmap_map(rcur, bno, blockcount, unwritten, &oinfo);
                break;
+       case XFS_RMAP_MAP_SHARED:
+               error = xfs_rmap_map_shared(rcur, bno, blockcount, unwritten,
+                               &oinfo);
+               break;
        case XFS_RMAP_FREE:
        case XFS_RMAP_UNMAP:
                error = xfs_rmap_unmap(rcur, bno, blockcount, unwritten,
                                &oinfo);
                break;
+       case XFS_RMAP_UNMAP_SHARED:
+               error = xfs_rmap_unmap_shared(rcur, bno, blockcount, unwritten,
+                               &oinfo);
+               break;
        case XFS_RMAP_CONVERT:
                error = xfs_rmap_convert(rcur, bno, blockcount, !unwritten,
                                &oinfo);
                break;
+       case XFS_RMAP_CONVERT_SHARED:
+               error = xfs_rmap_convert_shared(rcur, bno, blockcount,
+                               !unwritten, &oinfo);
+               break;
        default:
                ASSERT(0);
                error = -EFSCORRUPTED;
@@ -1263,9 +2153,10 @@ out_cur:
  */
 static bool
 xfs_rmap_update_is_needed(
-       struct xfs_mount        *mp)
+       struct xfs_mount        *mp,
+       int                     whichfork)
 {
-       return xfs_sb_version_hasrmapbt(&mp->m_sb);
+       return xfs_sb_version_hasrmapbt(&mp->m_sb) && whichfork != XFS_COW_FORK;
 }
 
 /*
@@ -1311,10 +2202,11 @@ xfs_rmap_map_extent(
        int                     whichfork,
        struct xfs_bmbt_irec    *PREV)
 {
-       if (!xfs_rmap_update_is_needed(mp))
+       if (!xfs_rmap_update_is_needed(mp, whichfork))
                return 0;
 
-       return __xfs_rmap_add(mp, dfops, XFS_RMAP_MAP, ip->i_ino,
+       return __xfs_rmap_add(mp, dfops, xfs_is_reflink_inode(ip) ?
+                       XFS_RMAP_MAP_SHARED : XFS_RMAP_MAP, ip->i_ino,
                        whichfork, PREV);
 }
 
@@ -1327,10 +2219,11 @@ xfs_rmap_unmap_extent(
        int                     whichfork,
        struct xfs_bmbt_irec    *PREV)
 {
-       if (!xfs_rmap_update_is_needed(mp))
+       if (!xfs_rmap_update_is_needed(mp, whichfork))
                return 0;
 
-       return __xfs_rmap_add(mp, dfops, XFS_RMAP_UNMAP, ip->i_ino,
+       return __xfs_rmap_add(mp, dfops, xfs_is_reflink_inode(ip) ?
+                       XFS_RMAP_UNMAP_SHARED : XFS_RMAP_UNMAP, ip->i_ino,
                        whichfork, PREV);
 }
 
@@ -1343,10 +2236,11 @@ xfs_rmap_convert_extent(
        int                     whichfork,
        struct xfs_bmbt_irec    *PREV)
 {
-       if (!xfs_rmap_update_is_needed(mp))
+       if (!xfs_rmap_update_is_needed(mp, whichfork))
                return 0;
 
-       return __xfs_rmap_add(mp, dfops, XFS_RMAP_CONVERT, ip->i_ino,
+       return __xfs_rmap_add(mp, dfops, xfs_is_reflink_inode(ip) ?
+                       XFS_RMAP_CONVERT_SHARED : XFS_RMAP_CONVERT, ip->i_ino,
                        whichfork, PREV);
 }
 
@@ -1362,7 +2256,7 @@ xfs_rmap_alloc_extent(
 {
        struct xfs_bmbt_irec    bmap;
 
-       if (!xfs_rmap_update_is_needed(mp))
+       if (!xfs_rmap_update_is_needed(mp, XFS_DATA_FORK))
                return 0;
 
        bmap.br_startblock = XFS_AGB_TO_FSB(mp, agno, bno);
@@ -1386,7 +2280,7 @@ xfs_rmap_free_extent(
 {
        struct xfs_bmbt_irec    bmap;
 
-       if (!xfs_rmap_update_is_needed(mp))
+       if (!xfs_rmap_update_is_needed(mp, XFS_DATA_FORK))
                return 0;
 
        bmap.br_startblock = XFS_AGB_TO_FSB(mp, agno, bno);
index 71cf99a4acbaee9a898c671a37bc66830fbaa831..789930599339058ed8db437a6ff5be061095877c 100644 (file)
@@ -206,4 +206,11 @@ int xfs_rmap_finish_one(struct xfs_trans *tp, enum xfs_rmap_intent_type type,
                xfs_fsblock_t startblock, xfs_filblks_t blockcount,
                xfs_exntst_t state, struct xfs_btree_cur **pcur);
 
+int xfs_rmap_find_left_neighbor(struct xfs_btree_cur *cur, xfs_agblock_t bno,
+               uint64_t owner, uint64_t offset, unsigned int flags,
+               struct xfs_rmap_irec *irec, int *stat);
+int xfs_rmap_lookup_le_range(struct xfs_btree_cur *cur, xfs_agblock_t bno,
+               uint64_t owner, uint64_t offset, unsigned int flags,
+               struct xfs_rmap_irec *irec, int *stat);
+
 #endif /* __XFS_RMAP_H__ */
index 17b8eeb34ac89ffb9260d7ac1c3cf342148eb1da..83e672ff7577e9040d22668a308097922b998cab 100644 (file)
@@ -35,6 +35,7 @@
 #include "xfs_cksum.h"
 #include "xfs_error.h"
 #include "xfs_extent_busy.h"
+#include "xfs_ag_resv.h"
 
 /*
  * Reverse map btree.
@@ -512,6 +513,83 @@ void
 xfs_rmapbt_compute_maxlevels(
        struct xfs_mount                *mp)
 {
-       mp->m_rmap_maxlevels = xfs_btree_compute_maxlevels(mp,
-                       mp->m_rmap_mnr, mp->m_sb.sb_agblocks);
+       /*
+        * On a non-reflink filesystem, the maximum number of rmap
+        * records is the number of blocks in the AG, hence the max
+        * rmapbt height is log_$maxrecs($agblocks).  However, with
+        * reflink each AG block can have up to 2^32 (per the refcount
+        * record format) owners, which means that theoretically we
+        * could face up to 2^64 rmap records.
+        *
+        * That effectively means that the max rmapbt height must be
+        * XFS_BTREE_MAXLEVELS.  "Fortunately" we'll run out of AG
+        * blocks to feed the rmapbt long before the rmapbt reaches
+        * maximum height.  The reflink code uses ag_resv_critical to
+        * disallow reflinking when less than 10% of the per-AG metadata
+        * block reservation since the fallback is a regular file copy.
+        */
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               mp->m_rmap_maxlevels = XFS_BTREE_MAXLEVELS;
+       else
+               mp->m_rmap_maxlevels = xfs_btree_compute_maxlevels(mp,
+                               mp->m_rmap_mnr, mp->m_sb.sb_agblocks);
+}
+
+/* Calculate the refcount btree size for some records. */
+xfs_extlen_t
+xfs_rmapbt_calc_size(
+       struct xfs_mount        *mp,
+       unsigned long long      len)
+{
+       return xfs_btree_calc_size(mp, mp->m_rmap_mnr, len);
+}
+
+/*
+ * Calculate the maximum refcount btree size.
+ */
+xfs_extlen_t
+xfs_rmapbt_max_size(
+       struct xfs_mount        *mp)
+{
+       /* Bail out if we're uninitialized, which can happen in mkfs. */
+       if (mp->m_rmap_mxr[0] == 0)
+               return 0;
+
+       return xfs_rmapbt_calc_size(mp, mp->m_sb.sb_agblocks);
+}
+
+/*
+ * Figure out how many blocks to reserve and how many are used by this btree.
+ */
+int
+xfs_rmapbt_calc_reserves(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno,
+       xfs_extlen_t            *ask,
+       xfs_extlen_t            *used)
+{
+       struct xfs_buf          *agbp;
+       struct xfs_agf          *agf;
+       xfs_extlen_t            pool_len;
+       xfs_extlen_t            tree_len;
+       int                     error;
+
+       if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
+               return 0;
+
+       /* Reserve 1% of the AG or enough for 1 block per record. */
+       pool_len = max(mp->m_sb.sb_agblocks / 100, xfs_rmapbt_max_size(mp));
+       *ask += pool_len;
+
+       error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+       if (error)
+               return error;
+
+       agf = XFS_BUF_TO_AGF(agbp);
+       tree_len = be32_to_cpu(agf->agf_rmap_blocks);
+       xfs_buf_relse(agbp);
+
+       *used += tree_len;
+
+       return error;
 }
index e73a55357dabe1f2a3f3c54bda6e5f49f52a4188..2a9ac472fb15a2408ca6d58addc6a5d439b61fe9 100644 (file)
@@ -58,4 +58,11 @@ struct xfs_btree_cur *xfs_rmapbt_init_cursor(struct xfs_mount *mp,
 int xfs_rmapbt_maxrecs(struct xfs_mount *mp, int blocklen, int leaf);
 extern void xfs_rmapbt_compute_maxlevels(struct xfs_mount *mp);
 
+extern xfs_extlen_t xfs_rmapbt_calc_size(struct xfs_mount *mp,
+               unsigned long long len);
+extern xfs_extlen_t xfs_rmapbt_max_size(struct xfs_mount *mp);
+
+extern int xfs_rmapbt_calc_reserves(struct xfs_mount *mp,
+               xfs_agnumber_t agno, xfs_extlen_t *ask, xfs_extlen_t *used);
+
 #endif /* __XFS_RMAP_BTREE_H__ */
index 4aecc5fefe9656e7e1f3812fc52c80cfc7e8eb9b..a70aec9106263f3e45e454e6fa3ef5ebcdac26ea 100644 (file)
@@ -38,6 +38,8 @@
 #include "xfs_ialloc_btree.h"
 #include "xfs_log.h"
 #include "xfs_rmap_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_refcount_btree.h"
 
 /*
  * Physical superblock buffer manipulations. Shared with libxfs in userspace.
@@ -737,6 +739,13 @@ xfs_sb_mount_common(
        mp->m_rmap_mnr[0] = mp->m_rmap_mxr[0] / 2;
        mp->m_rmap_mnr[1] = mp->m_rmap_mxr[1] / 2;
 
+       mp->m_refc_mxr[0] = xfs_refcountbt_maxrecs(mp, sbp->sb_blocksize,
+                       true);
+       mp->m_refc_mxr[1] = xfs_refcountbt_maxrecs(mp, sbp->sb_blocksize,
+                       false);
+       mp->m_refc_mnr[0] = mp->m_refc_mxr[0] / 2;
+       mp->m_refc_mnr[1] = mp->m_refc_mxr[1] / 2;
+
        mp->m_bsize = XFS_FSB_TO_BB(mp, 1);
        mp->m_ialloc_inos = (int)MAX((__uint16_t)XFS_INODES_PER_CHUNK,
                                        sbp->sb_inopblock);
index 0c5b30bd884cdce801780290935a5deb0c7d9de2..c6f4eb46fe263e0b3faf8e2a33e93cbf8984c976 100644 (file)
@@ -39,6 +39,7 @@ extern const struct xfs_buf_ops xfs_agf_buf_ops;
 extern const struct xfs_buf_ops xfs_agfl_buf_ops;
 extern const struct xfs_buf_ops xfs_allocbt_buf_ops;
 extern const struct xfs_buf_ops xfs_rmapbt_buf_ops;
+extern const struct xfs_buf_ops xfs_refcountbt_buf_ops;
 extern const struct xfs_buf_ops xfs_attr3_leaf_buf_ops;
 extern const struct xfs_buf_ops xfs_attr3_rmt_buf_ops;
 extern const struct xfs_buf_ops xfs_bmbt_buf_ops;
@@ -122,6 +123,7 @@ int xfs_log_calc_minimum_size(struct xfs_mount *);
 #define        XFS_INO_REF             2
 #define        XFS_ATTR_BTREE_REF      1
 #define        XFS_DQUOT_REF           1
+#define        XFS_REFC_BTREE_REF      1
 
 /*
  * Flags for xfs_trans_ichgtime().
index 301ef2f4dbd6258f8981aa2d91bed524a8ce9110..b456cca1bfb23fbf6f318648949ea3d9fe73cad4 100644 (file)
@@ -67,13 +67,14 @@ xfs_calc_buf_res(
  * Per-extent log reservation for the btree changes involved in freeing or
  * allocating an extent.  In classic XFS there were two trees that will be
  * modified (bnobt + cntbt).  With rmap enabled, there are three trees
- * (rmapbt).  The number of blocks reserved is based on the formula:
+ * (rmapbt).  With reflink, there are four trees (refcountbt).  The number of
+ * blocks reserved is based on the formula:
  *
  * num trees * ((2 blocks/level * max depth) - 1)
  *
  * Keep in mind that max depth is calculated separately for each type of tree.
  */
-static uint
+uint
 xfs_allocfree_log_count(
        struct xfs_mount *mp,
        uint            num_ops)
@@ -83,6 +84,8 @@ xfs_allocfree_log_count(
        blocks = num_ops * 2 * (2 * mp->m_ag_maxlevels - 1);
        if (xfs_sb_version_hasrmapbt(&mp->m_sb))
                blocks += num_ops * (2 * mp->m_rmap_maxlevels - 1);
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               blocks += num_ops * (2 * mp->m_refc_maxlevels - 1);
 
        return blocks;
 }
@@ -809,11 +812,18 @@ xfs_trans_resv_calc(
         * require a permanent reservation on space.
         */
        resp->tr_write.tr_logres = xfs_calc_write_reservation(mp);
-       resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT_REFLINK;
+       else
+               resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
        resp->tr_write.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
        resp->tr_itruncate.tr_logres = xfs_calc_itruncate_reservation(mp);
-       resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               resp->tr_itruncate.tr_logcount =
+                               XFS_ITRUNCATE_LOG_COUNT_REFLINK;
+       else
+               resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
        resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
        resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
@@ -870,7 +880,10 @@ xfs_trans_resv_calc(
        resp->tr_growrtalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
        resp->tr_qm_dqalloc.tr_logres = xfs_calc_qm_dqalloc_reservation(mp);
-       resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT_REFLINK;
+       else
+               resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
        resp->tr_qm_dqalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
        /*
index 0eb46ed6d404da7d3076e8338f56289ae7f83151..b7e5357d060a4295e7452b2a5f045e3d853bfc9d 100644 (file)
@@ -87,6 +87,7 @@ struct xfs_trans_resv {
 #define        XFS_DEFAULT_LOG_COUNT           1
 #define        XFS_DEFAULT_PERM_LOG_COUNT      2
 #define        XFS_ITRUNCATE_LOG_COUNT         2
+#define        XFS_ITRUNCATE_LOG_COUNT_REFLINK 8
 #define XFS_INACTIVE_LOG_COUNT         2
 #define        XFS_CREATE_LOG_COUNT            2
 #define        XFS_CREATE_TMPFILE_LOG_COUNT    2
@@ -96,11 +97,13 @@ struct xfs_trans_resv {
 #define        XFS_LINK_LOG_COUNT              2
 #define        XFS_RENAME_LOG_COUNT            2
 #define        XFS_WRITE_LOG_COUNT             2
+#define        XFS_WRITE_LOG_COUNT_REFLINK     8
 #define        XFS_ADDAFORK_LOG_COUNT          2
 #define        XFS_ATTRINVAL_LOG_COUNT         1
 #define        XFS_ATTRSET_LOG_COUNT           3
 #define        XFS_ATTRRM_LOG_COUNT            3
 
 void xfs_trans_resv_calc(struct xfs_mount *mp, struct xfs_trans_resv *resp);
+uint xfs_allocfree_log_count(struct xfs_mount *mp, uint num_ops);
 
 #endif /* __XFS_TRANS_RESV_H__ */
index 41e0428d8175a2ab7ea4be8d7f67ce932a7e3a3f..7917f6e44286a4591e9109138a7be07dc1a18eb1 100644 (file)
@@ -21,6 +21,8 @@
 /*
  * Components of space reservations.
  */
+#define XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp)    \
+               (((mp)->m_rmap_mxr[0]) - ((mp)->m_rmap_mnr[0]))
 #define XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp)    \
                (((mp)->m_alloc_mxr[0]) - ((mp)->m_alloc_mnr[0]))
 #define        XFS_EXTENTADD_SPACE_RES(mp,w)   (XFS_BM_MAXLEVELS(mp,w) - 1)
        (((b + XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp) - 1) / \
          XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp)) * \
          XFS_EXTENTADD_SPACE_RES(mp,w))
+#define XFS_SWAP_RMAP_SPACE_RES(mp,b,w)\
+       (((b + XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp) - 1) / \
+         XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp)) * \
+         XFS_EXTENTADD_SPACE_RES(mp,w) + \
+        ((b + XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp) - 1) / \
+         XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp)) * \
+         (mp)->m_rmap_maxlevels)
 #define        XFS_DAENTER_1B(mp,w)    \
        ((w) == XFS_DATA_FORK ? (mp)->m_dir_geo->fsbcount : 1)
 #define        XFS_DAENTER_DBS(mp,w)   \
index 3d503647f26b6924ecbe9b702d076bf29220830a..8d74870468c24bb5661a0afa0e7ab436f8b4ded0 100644 (file)
@@ -90,6 +90,7 @@ typedef __int64_t     xfs_sfiloff_t;  /* signed block number in a file */
  */
 #define        XFS_DATA_FORK   0
 #define        XFS_ATTR_FORK   1
+#define        XFS_COW_FORK    2
 
 /*
  * Min numbers of data/attr fork btree root pointers.
@@ -109,7 +110,7 @@ typedef enum {
 
 typedef enum {
        XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_RMAPi, XFS_BTNUM_BMAPi,
-       XFS_BTNUM_INOi, XFS_BTNUM_FINOi, XFS_BTNUM_MAX
+       XFS_BTNUM_INOi, XFS_BTNUM_FINOi, XFS_BTNUM_REFCi, XFS_BTNUM_MAX
 } xfs_btnum_t;
 
 struct xfs_name {
index 4a28fa91e3b1aa74464620cb5d0d9c746893ccb0..3e57a56cf8294770475e5edd04d03ee5565516fa 100644 (file)
@@ -31,6 +31,7 @@
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
 #include "xfs_bmap_btree.h"
+#include "xfs_reflink.h"
 #include <linux/gfp.h>
 #include <linux/mpage.h>
 #include <linux/pagevec.h>
@@ -39,6 +40,7 @@
 /* flags for direct write completions */
 #define XFS_DIO_FLAG_UNWRITTEN (1 << 0)
 #define XFS_DIO_FLAG_APPEND    (1 << 1)
+#define XFS_DIO_FLAG_COW       (1 << 2)
 
 /*
  * structure owned by writepages passed to individual writepage calls
@@ -286,6 +288,25 @@ xfs_end_io(
        if (XFS_FORCED_SHUTDOWN(ip->i_mount))
                error = -EIO;
 
+       /*
+        * For a CoW extent, we need to move the mapping from the CoW fork
+        * to the data fork.  If instead an error happened, just dump the
+        * new blocks.
+        */
+       if (ioend->io_type == XFS_IO_COW) {
+               if (error)
+                       goto done;
+               if (ioend->io_bio->bi_error) {
+                       error = xfs_reflink_cancel_cow_range(ip,
+                                       ioend->io_offset, ioend->io_size);
+                       goto done;
+               }
+               error = xfs_reflink_end_cow(ip, ioend->io_offset,
+                               ioend->io_size);
+               if (error)
+                       goto done;
+       }
+
        /*
         * For unwritten extents we need to issue transactions to convert a
         * range to normal written extens after the data I/O has finished.
@@ -301,7 +322,8 @@ xfs_end_io(
        } else if (ioend->io_append_trans) {
                error = xfs_setfilesize_ioend(ioend, error);
        } else {
-               ASSERT(!xfs_ioend_is_append(ioend));
+               ASSERT(!xfs_ioend_is_append(ioend) ||
+                      ioend->io_type == XFS_IO_COW);
        }
 
 done:
@@ -315,7 +337,7 @@ xfs_end_bio(
        struct xfs_ioend        *ioend = bio->bi_private;
        struct xfs_mount        *mp = XFS_I(ioend->io_inode)->i_mount;
 
-       if (ioend->io_type == XFS_IO_UNWRITTEN)
+       if (ioend->io_type == XFS_IO_UNWRITTEN || ioend->io_type == XFS_IO_COW)
                queue_work(mp->m_unwritten_workqueue, &ioend->io_work);
        else if (ioend->io_append_trans)
                queue_work(mp->m_data_workqueue, &ioend->io_work);
@@ -341,6 +363,7 @@ xfs_map_blocks(
        if (XFS_FORCED_SHUTDOWN(mp))
                return -EIO;
 
+       ASSERT(type != XFS_IO_COW);
        if (type == XFS_IO_UNWRITTEN)
                bmapi_flags |= XFS_BMAPI_IGSTATE;
 
@@ -355,6 +378,13 @@ xfs_map_blocks(
        offset_fsb = XFS_B_TO_FSBT(mp, offset);
        error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
                                imap, &nimaps, bmapi_flags);
+       /*
+        * Truncate an overwrite extent if there's a pending CoW
+        * reservation before the end of this extent.  This forces us
+        * to come back to writepage to take care of the CoW.
+        */
+       if (nimaps && type == XFS_IO_OVERWRITE)
+               xfs_reflink_trim_irec_to_next_cow(ip, offset_fsb, imap);
        xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
        if (error)
@@ -362,7 +392,8 @@ xfs_map_blocks(
 
        if (type == XFS_IO_DELALLOC &&
            (!nimaps || isnullstartblock(imap->br_startblock))) {
-               error = xfs_iomap_write_allocate(ip, offset, imap);
+               error = xfs_iomap_write_allocate(ip, XFS_DATA_FORK, offset,
+                               imap);
                if (!error)
                        trace_xfs_map_blocks_alloc(ip, offset, count, type, imap);
                return error;
@@ -737,6 +768,56 @@ out_invalidate:
        return;
 }
 
+static int
+xfs_map_cow(
+       struct xfs_writepage_ctx *wpc,
+       struct inode            *inode,
+       loff_t                  offset,
+       unsigned int            *new_type)
+{
+       struct xfs_inode        *ip = XFS_I(inode);
+       struct xfs_bmbt_irec    imap;
+       bool                    is_cow = false, need_alloc = false;
+       int                     error;
+
+       /*
+        * If we already have a valid COW mapping keep using it.
+        */
+       if (wpc->io_type == XFS_IO_COW) {
+               wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap, offset);
+               if (wpc->imap_valid) {
+                       *new_type = XFS_IO_COW;
+                       return 0;
+               }
+       }
+
+       /*
+        * Else we need to check if there is a COW mapping at this offset.
+        */
+       xfs_ilock(ip, XFS_ILOCK_SHARED);
+       is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap, &need_alloc);
+       xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+       if (!is_cow)
+               return 0;
+
+       /*
+        * And if the COW mapping has a delayed extent here we need to
+        * allocate real space for it now.
+        */
+       if (need_alloc) {
+               error = xfs_iomap_write_allocate(ip, XFS_COW_FORK, offset,
+                               &imap);
+               if (error)
+                       return error;
+       }
+
+       wpc->io_type = *new_type = XFS_IO_COW;
+       wpc->imap_valid = true;
+       wpc->imap = imap;
+       return 0;
+}
+
 /*
  * We implement an immediate ioend submission policy here to avoid needing to
  * chain multiple ioends and hence nest mempool allocations which can violate
@@ -769,6 +850,7 @@ xfs_writepage_map(
        int                     error = 0;
        int                     count = 0;
        int                     uptodate = 1;
+       unsigned int            new_type;
 
        bh = head = page_buffers(page);
        offset = page_offset(page);
@@ -789,22 +871,13 @@ xfs_writepage_map(
                        continue;
                }
 
-               if (buffer_unwritten(bh)) {
-                       if (wpc->io_type != XFS_IO_UNWRITTEN) {
-                               wpc->io_type = XFS_IO_UNWRITTEN;
-                               wpc->imap_valid = false;
-                       }
-               } else if (buffer_delay(bh)) {
-                       if (wpc->io_type != XFS_IO_DELALLOC) {
-                               wpc->io_type = XFS_IO_DELALLOC;
-                               wpc->imap_valid = false;
-                       }
-               } else if (buffer_uptodate(bh)) {
-                       if (wpc->io_type != XFS_IO_OVERWRITE) {
-                               wpc->io_type = XFS_IO_OVERWRITE;
-                               wpc->imap_valid = false;
-                       }
-               } else {
+               if (buffer_unwritten(bh))
+                       new_type = XFS_IO_UNWRITTEN;
+               else if (buffer_delay(bh))
+                       new_type = XFS_IO_DELALLOC;
+               else if (buffer_uptodate(bh))
+                       new_type = XFS_IO_OVERWRITE;
+               else {
                        if (PageUptodate(page))
                                ASSERT(buffer_mapped(bh));
                        /*
@@ -817,6 +890,17 @@ xfs_writepage_map(
                        continue;
                }
 
+               if (xfs_is_reflink_inode(XFS_I(inode))) {
+                       error = xfs_map_cow(wpc, inode, offset, &new_type);
+                       if (error)
+                               goto out;
+               }
+
+               if (wpc->io_type != new_type) {
+                       wpc->io_type = new_type;
+                       wpc->imap_valid = false;
+               }
+
                if (wpc->imap_valid)
                        wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap,
                                                         offset);
@@ -1107,18 +1191,24 @@ xfs_map_direct(
        struct inode            *inode,
        struct buffer_head      *bh_result,
        struct xfs_bmbt_irec    *imap,
-       xfs_off_t               offset)
+       xfs_off_t               offset,
+       bool                    is_cow)
 {
        uintptr_t               *flags = (uintptr_t *)&bh_result->b_private;
        xfs_off_t               size = bh_result->b_size;
 
        trace_xfs_get_blocks_map_direct(XFS_I(inode), offset, size,
-               ISUNWRITTEN(imap) ? XFS_IO_UNWRITTEN : XFS_IO_OVERWRITE, imap);
+               ISUNWRITTEN(imap) ? XFS_IO_UNWRITTEN : is_cow ? XFS_IO_COW :
+               XFS_IO_OVERWRITE, imap);
 
        if (ISUNWRITTEN(imap)) {
                *flags |= XFS_DIO_FLAG_UNWRITTEN;
                set_buffer_defer_completion(bh_result);
-       } else if (offset + size > i_size_read(inode) || offset + size < 0) {
+       } else if (is_cow) {
+               *flags |= XFS_DIO_FLAG_COW;
+               set_buffer_defer_completion(bh_result);
+       }
+       if (offset + size > i_size_read(inode) || offset + size < 0) {
                *flags |= XFS_DIO_FLAG_APPEND;
                set_buffer_defer_completion(bh_result);
        }
@@ -1164,6 +1254,44 @@ xfs_map_trim_size(
        bh_result->b_size = mapping_size;
 }
 
+/* Bounce unaligned directio writes to the page cache. */
+static int
+xfs_bounce_unaligned_dio_write(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           offset_fsb,
+       struct xfs_bmbt_irec    *imap)
+{
+       struct xfs_bmbt_irec    irec;
+       xfs_fileoff_t           delta;
+       bool                    shared;
+       bool                    x;
+       int                     error;
+
+       irec = *imap;
+       if (offset_fsb > irec.br_startoff) {
+               delta = offset_fsb - irec.br_startoff;
+               irec.br_blockcount -= delta;
+               irec.br_startblock += delta;
+               irec.br_startoff = offset_fsb;
+       }
+       error = xfs_reflink_trim_around_shared(ip, &irec, &shared, &x);
+       if (error)
+               return error;
+
+       /*
+        * We're here because we're trying to do a directio write to a
+        * region that isn't aligned to a filesystem block.  If any part
+        * of the extent is shared, fall back to buffered mode to handle
+        * the RMW.  This is done by returning -EREMCHG ("remote addr
+        * changed"), which is caught further up the call stack.
+        */
+       if (shared) {
+               trace_xfs_reflink_bounce_dio_write(ip, imap);
+               return -EREMCHG;
+       }
+       return 0;
+}
+
 STATIC int
 __xfs_get_blocks(
        struct inode            *inode,
@@ -1183,6 +1311,8 @@ __xfs_get_blocks(
        xfs_off_t               offset;
        ssize_t                 size;
        int                     new = 0;
+       bool                    is_cow = false;
+       bool                    need_alloc = false;
 
        BUG_ON(create && !direct);
 
@@ -1208,8 +1338,26 @@ __xfs_get_blocks(
        end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + size);
        offset_fsb = XFS_B_TO_FSBT(mp, offset);
 
-       error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
-                               &imap, &nimaps, XFS_BMAPI_ENTIRE);
+       if (create && direct && xfs_is_reflink_inode(ip))
+               is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap,
+                                       &need_alloc);
+       if (!is_cow) {
+               error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
+                                       &imap, &nimaps, XFS_BMAPI_ENTIRE);
+               /*
+                * Truncate an overwrite extent if there's a pending CoW
+                * reservation before the end of this extent.  This
+                * forces us to come back to get_blocks to take care of
+                * the CoW.
+                */
+               if (create && direct && nimaps &&
+                   imap.br_startblock != HOLESTARTBLOCK &&
+                   imap.br_startblock != DELAYSTARTBLOCK &&
+                   !ISUNWRITTEN(&imap))
+                       xfs_reflink_trim_irec_to_next_cow(ip, offset_fsb,
+                                       &imap);
+       }
+       ASSERT(!need_alloc);
        if (error)
                goto out_unlock;
 
@@ -1261,6 +1409,13 @@ __xfs_get_blocks(
        if (imap.br_startblock != HOLESTARTBLOCK &&
            imap.br_startblock != DELAYSTARTBLOCK &&
            (create || !ISUNWRITTEN(&imap))) {
+               if (create && direct && !is_cow) {
+                       error = xfs_bounce_unaligned_dio_write(ip, offset_fsb,
+                                       &imap);
+                       if (error)
+                               return error;
+               }
+
                xfs_map_buffer(inode, bh_result, &imap, offset);
                if (ISUNWRITTEN(&imap))
                        set_buffer_unwritten(bh_result);
@@ -1269,7 +1424,8 @@ __xfs_get_blocks(
                        if (dax_fault)
                                ASSERT(!ISUNWRITTEN(&imap));
                        else
-                               xfs_map_direct(inode, bh_result, &imap, offset);
+                               xfs_map_direct(inode, bh_result, &imap, offset,
+                                               is_cow);
                }
        }
 
@@ -1391,11 +1547,14 @@ xfs_end_io_direct_write(
                i_size_write(inode, offset + size);
        spin_unlock(&ip->i_flags_lock);
 
+       if (flags & XFS_DIO_FLAG_COW)
+               error = xfs_reflink_end_cow(ip, offset, size);
        if (flags & XFS_DIO_FLAG_UNWRITTEN) {
                trace_xfs_end_io_direct_write_unwritten(ip, offset, size);
 
                error = xfs_iomap_write_unwritten(ip, offset, size);
-       } else if (flags & XFS_DIO_FLAG_APPEND) {
+       }
+       if (flags & XFS_DIO_FLAG_APPEND) {
                trace_xfs_end_io_direct_write_append(ip, offset, size);
 
                error = xfs_setfilesize(ip, offset, size);
@@ -1425,6 +1584,17 @@ xfs_vm_bmap(
 
        trace_xfs_vm_bmap(XFS_I(inode));
        xfs_ilock(ip, XFS_IOLOCK_SHARED);
+
+       /*
+        * The swap code (ab-)uses ->bmap to get a block mapping and then
+        * bypasseÑ• the file system for actual I/O.  We really can't allow
+        * that on reflinks inodes, so we have to skip out here.  And yes,
+        * 0 is the magic code for a bmap error..
+        */
+       if (xfs_is_reflink_inode(ip)) {
+               xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+               return 0;
+       }
        filemap_write_and_wait(mapping);
        xfs_iunlock(ip, XFS_IOLOCK_SHARED);
        return generic_block_bmap(mapping, block, xfs_get_blocks);
index 1950e3bca2ac24ce981ddafa25658d80e7db71b7..b3c6634f9518484d3e0d1691a53e7ff705a5f380 100644 (file)
@@ -28,13 +28,15 @@ enum {
        XFS_IO_DELALLOC,        /* covers delalloc region */
        XFS_IO_UNWRITTEN,       /* covers allocated but uninitialized data */
        XFS_IO_OVERWRITE,       /* covers already allocated extent */
+       XFS_IO_COW,             /* covers copy-on-write extent */
 };
 
 #define XFS_IO_TYPES \
        { XFS_IO_INVALID,               "invalid" }, \
        { XFS_IO_DELALLOC,              "delalloc" }, \
        { XFS_IO_UNWRITTEN,             "unwritten" }, \
-       { XFS_IO_OVERWRITE,             "overwrite" }
+       { XFS_IO_OVERWRITE,             "overwrite" }, \
+       { XFS_IO_COW,                   "CoW" }
 
 /*
  * Structure for buffered I/O completions.
diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c
new file mode 100644 (file)
index 0000000..9bf57c7
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_bit.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_inode.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_buf_item.h"
+#include "xfs_bmap_item.h"
+#include "xfs_log.h"
+#include "xfs_bmap.h"
+#include "xfs_icache.h"
+#include "xfs_trace.h"
+
+
+kmem_zone_t    *xfs_bui_zone;
+kmem_zone_t    *xfs_bud_zone;
+
+static inline struct xfs_bui_log_item *BUI_ITEM(struct xfs_log_item *lip)
+{
+       return container_of(lip, struct xfs_bui_log_item, bui_item);
+}
+
+void
+xfs_bui_item_free(
+       struct xfs_bui_log_item *buip)
+{
+       kmem_zone_free(xfs_bui_zone, buip);
+}
+
+STATIC void
+xfs_bui_item_size(
+       struct xfs_log_item     *lip,
+       int                     *nvecs,
+       int                     *nbytes)
+{
+       struct xfs_bui_log_item *buip = BUI_ITEM(lip);
+
+       *nvecs += 1;
+       *nbytes += xfs_bui_log_format_sizeof(buip->bui_format.bui_nextents);
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given bui log item. We use only 1 iovec, and we point that
+ * at the bui_log_format structure embedded in the bui item.
+ * It is at this point that we assert that all of the extent
+ * slots in the bui item have been filled.
+ */
+STATIC void
+xfs_bui_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_vec      *lv)
+{
+       struct xfs_bui_log_item *buip = BUI_ITEM(lip);
+       struct xfs_log_iovec    *vecp = NULL;
+
+       ASSERT(atomic_read(&buip->bui_next_extent) ==
+                       buip->bui_format.bui_nextents);
+
+       buip->bui_format.bui_type = XFS_LI_BUI;
+       buip->bui_format.bui_size = 1;
+
+       xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_BUI_FORMAT, &buip->bui_format,
+                       xfs_bui_log_format_sizeof(buip->bui_format.bui_nextents));
+}
+
+/*
+ * Pinning has no meaning for an bui item, so just return.
+ */
+STATIC void
+xfs_bui_item_pin(
+       struct xfs_log_item     *lip)
+{
+}
+
+/*
+ * The unpin operation is the last place an BUI is manipulated in the log. It is
+ * either inserted in the AIL or aborted in the event of a log I/O error. In
+ * either case, the BUI transaction has been successfully committed to make it
+ * this far. Therefore, we expect whoever committed the BUI to either construct
+ * and commit the BUD or drop the BUD's reference in the event of error. Simply
+ * drop the log's BUI reference now that the log is done with it.
+ */
+STATIC void
+xfs_bui_item_unpin(
+       struct xfs_log_item     *lip,
+       int                     remove)
+{
+       struct xfs_bui_log_item *buip = BUI_ITEM(lip);
+
+       xfs_bui_release(buip);
+}
+
+/*
+ * BUI items have no locking or pushing.  However, since BUIs are pulled from
+ * the AIL when their corresponding BUDs are committed to disk, their situation
+ * is very similar to being pinned.  Return XFS_ITEM_PINNED so that the caller
+ * will eventually flush the log.  This should help in getting the BUI out of
+ * the AIL.
+ */
+STATIC uint
+xfs_bui_item_push(
+       struct xfs_log_item     *lip,
+       struct list_head        *buffer_list)
+{
+       return XFS_ITEM_PINNED;
+}
+
+/*
+ * The BUI has been either committed or aborted if the transaction has been
+ * cancelled. If the transaction was cancelled, an BUD isn't going to be
+ * constructed and thus we free the BUI here directly.
+ */
+STATIC void
+xfs_bui_item_unlock(
+       struct xfs_log_item     *lip)
+{
+       if (lip->li_flags & XFS_LI_ABORTED)
+               xfs_bui_item_free(BUI_ITEM(lip));
+}
+
+/*
+ * The BUI is logged only once and cannot be moved in the log, so simply return
+ * the lsn at which it's been logged.
+ */
+STATIC xfs_lsn_t
+xfs_bui_item_committed(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+       return lsn;
+}
+
+/*
+ * The BUI dependency tracking op doesn't do squat.  It can't because
+ * it doesn't know where the free extent is coming from.  The dependency
+ * tracking has to be handled by the "enclosing" metadata object.  For
+ * example, for inodes, the inode is locked throughout the extent freeing
+ * so the dependency should be recorded there.
+ */
+STATIC void
+xfs_bui_item_committing(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+}
+
+/*
+ * This is the ops vector shared by all bui log items.
+ */
+static const struct xfs_item_ops xfs_bui_item_ops = {
+       .iop_size       = xfs_bui_item_size,
+       .iop_format     = xfs_bui_item_format,
+       .iop_pin        = xfs_bui_item_pin,
+       .iop_unpin      = xfs_bui_item_unpin,
+       .iop_unlock     = xfs_bui_item_unlock,
+       .iop_committed  = xfs_bui_item_committed,
+       .iop_push       = xfs_bui_item_push,
+       .iop_committing = xfs_bui_item_committing,
+};
+
+/*
+ * Allocate and initialize an bui item with the given number of extents.
+ */
+struct xfs_bui_log_item *
+xfs_bui_init(
+       struct xfs_mount                *mp)
+
+{
+       struct xfs_bui_log_item         *buip;
+
+       buip = kmem_zone_zalloc(xfs_bui_zone, KM_SLEEP);
+
+       xfs_log_item_init(mp, &buip->bui_item, XFS_LI_BUI, &xfs_bui_item_ops);
+       buip->bui_format.bui_nextents = XFS_BUI_MAX_FAST_EXTENTS;
+       buip->bui_format.bui_id = (uintptr_t)(void *)buip;
+       atomic_set(&buip->bui_next_extent, 0);
+       atomic_set(&buip->bui_refcount, 2);
+
+       return buip;
+}
+
+/*
+ * Freeing the BUI requires that we remove it from the AIL if it has already
+ * been placed there. However, the BUI may not yet have been placed in the AIL
+ * when called by xfs_bui_release() from BUD processing due to the ordering of
+ * committed vs unpin operations in bulk insert operations. Hence the reference
+ * count to ensure only the last caller frees the BUI.
+ */
+void
+xfs_bui_release(
+       struct xfs_bui_log_item *buip)
+{
+       if (atomic_dec_and_test(&buip->bui_refcount)) {
+               xfs_trans_ail_remove(&buip->bui_item, SHUTDOWN_LOG_IO_ERROR);
+               xfs_bui_item_free(buip);
+       }
+}
+
+static inline struct xfs_bud_log_item *BUD_ITEM(struct xfs_log_item *lip)
+{
+       return container_of(lip, struct xfs_bud_log_item, bud_item);
+}
+
+STATIC void
+xfs_bud_item_size(
+       struct xfs_log_item     *lip,
+       int                     *nvecs,
+       int                     *nbytes)
+{
+       *nvecs += 1;
+       *nbytes += sizeof(struct xfs_bud_log_format);
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given bud log item. We use only 1 iovec, and we point that
+ * at the bud_log_format structure embedded in the bud item.
+ * It is at this point that we assert that all of the extent
+ * slots in the bud item have been filled.
+ */
+STATIC void
+xfs_bud_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_vec      *lv)
+{
+       struct xfs_bud_log_item *budp = BUD_ITEM(lip);
+       struct xfs_log_iovec    *vecp = NULL;
+
+       budp->bud_format.bud_type = XFS_LI_BUD;
+       budp->bud_format.bud_size = 1;
+
+       xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_BUD_FORMAT, &budp->bud_format,
+                       sizeof(struct xfs_bud_log_format));
+}
+
+/*
+ * Pinning has no meaning for an bud item, so just return.
+ */
+STATIC void
+xfs_bud_item_pin(
+       struct xfs_log_item     *lip)
+{
+}
+
+/*
+ * Since pinning has no meaning for an bud item, unpinning does
+ * not either.
+ */
+STATIC void
+xfs_bud_item_unpin(
+       struct xfs_log_item     *lip,
+       int                     remove)
+{
+}
+
+/*
+ * There isn't much you can do to push on an bud item.  It is simply stuck
+ * waiting for the log to be flushed to disk.
+ */
+STATIC uint
+xfs_bud_item_push(
+       struct xfs_log_item     *lip,
+       struct list_head        *buffer_list)
+{
+       return XFS_ITEM_PINNED;
+}
+
+/*
+ * The BUD is either committed or aborted if the transaction is cancelled. If
+ * the transaction is cancelled, drop our reference to the BUI and free the
+ * BUD.
+ */
+STATIC void
+xfs_bud_item_unlock(
+       struct xfs_log_item     *lip)
+{
+       struct xfs_bud_log_item *budp = BUD_ITEM(lip);
+
+       if (lip->li_flags & XFS_LI_ABORTED) {
+               xfs_bui_release(budp->bud_buip);
+               kmem_zone_free(xfs_bud_zone, budp);
+       }
+}
+
+/*
+ * When the bud item is committed to disk, all we need to do is delete our
+ * reference to our partner bui item and then free ourselves. Since we're
+ * freeing ourselves we must return -1 to keep the transaction code from
+ * further referencing this item.
+ */
+STATIC xfs_lsn_t
+xfs_bud_item_committed(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+       struct xfs_bud_log_item *budp = BUD_ITEM(lip);
+
+       /*
+        * Drop the BUI reference regardless of whether the BUD has been
+        * aborted. Once the BUD transaction is constructed, it is the sole
+        * responsibility of the BUD to release the BUI (even if the BUI is
+        * aborted due to log I/O error).
+        */
+       xfs_bui_release(budp->bud_buip);
+       kmem_zone_free(xfs_bud_zone, budp);
+
+       return (xfs_lsn_t)-1;
+}
+
+/*
+ * The BUD dependency tracking op doesn't do squat.  It can't because
+ * it doesn't know where the free extent is coming from.  The dependency
+ * tracking has to be handled by the "enclosing" metadata object.  For
+ * example, for inodes, the inode is locked throughout the extent freeing
+ * so the dependency should be recorded there.
+ */
+STATIC void
+xfs_bud_item_committing(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+}
+
+/*
+ * This is the ops vector shared by all bud log items.
+ */
+static const struct xfs_item_ops xfs_bud_item_ops = {
+       .iop_size       = xfs_bud_item_size,
+       .iop_format     = xfs_bud_item_format,
+       .iop_pin        = xfs_bud_item_pin,
+       .iop_unpin      = xfs_bud_item_unpin,
+       .iop_unlock     = xfs_bud_item_unlock,
+       .iop_committed  = xfs_bud_item_committed,
+       .iop_push       = xfs_bud_item_push,
+       .iop_committing = xfs_bud_item_committing,
+};
+
+/*
+ * Allocate and initialize an bud item with the given number of extents.
+ */
+struct xfs_bud_log_item *
+xfs_bud_init(
+       struct xfs_mount                *mp,
+       struct xfs_bui_log_item         *buip)
+
+{
+       struct xfs_bud_log_item *budp;
+
+       budp = kmem_zone_zalloc(xfs_bud_zone, KM_SLEEP);
+       xfs_log_item_init(mp, &budp->bud_item, XFS_LI_BUD, &xfs_bud_item_ops);
+       budp->bud_buip = buip;
+       budp->bud_format.bud_bui_id = buip->bui_format.bui_id;
+
+       return budp;
+}
+
+/*
+ * Process a bmap update intent item that was recovered from the log.
+ * We need to update some inode's bmbt.
+ */
+int
+xfs_bui_recover(
+       struct xfs_mount                *mp,
+       struct xfs_bui_log_item         *buip)
+{
+       int                             error = 0;
+       unsigned int                    bui_type;
+       struct xfs_map_extent           *bmap;
+       xfs_fsblock_t                   startblock_fsb;
+       xfs_fsblock_t                   inode_fsb;
+       bool                            op_ok;
+       struct xfs_bud_log_item         *budp;
+       enum xfs_bmap_intent_type       type;
+       int                             whichfork;
+       xfs_exntst_t                    state;
+       struct xfs_trans                *tp;
+       struct xfs_inode                *ip = NULL;
+       struct xfs_defer_ops            dfops;
+       xfs_fsblock_t                   firstfsb;
+
+       ASSERT(!test_bit(XFS_BUI_RECOVERED, &buip->bui_flags));
+
+       /* Only one mapping operation per BUI... */
+       if (buip->bui_format.bui_nextents != XFS_BUI_MAX_FAST_EXTENTS) {
+               set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);
+               xfs_bui_release(buip);
+               return -EIO;
+       }
+
+       /*
+        * First check the validity of the extent described by the
+        * BUI.  If anything is bad, then toss the BUI.
+        */
+       bmap = &buip->bui_format.bui_extents[0];
+       startblock_fsb = XFS_BB_TO_FSB(mp,
+                          XFS_FSB_TO_DADDR(mp, bmap->me_startblock));
+       inode_fsb = XFS_BB_TO_FSB(mp, XFS_FSB_TO_DADDR(mp,
+                       XFS_INO_TO_FSB(mp, bmap->me_owner)));
+       switch (bmap->me_flags & XFS_BMAP_EXTENT_TYPE_MASK) {
+       case XFS_BMAP_MAP:
+       case XFS_BMAP_UNMAP:
+               op_ok = true;
+               break;
+       default:
+               op_ok = false;
+               break;
+       }
+       if (!op_ok || startblock_fsb == 0 ||
+           bmap->me_len == 0 ||
+           inode_fsb == 0 ||
+           startblock_fsb >= mp->m_sb.sb_dblocks ||
+           bmap->me_len >= mp->m_sb.sb_agblocks ||
+           inode_fsb >= mp->m_sb.sb_dblocks ||
+           (bmap->me_flags & ~XFS_BMAP_EXTENT_FLAGS)) {
+               /*
+                * This will pull the BUI from the AIL and
+                * free the memory associated with it.
+                */
+               set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);
+               xfs_bui_release(buip);
+               return -EIO;
+       }
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
+       if (error)
+               return error;
+       budp = xfs_trans_get_bud(tp, buip);
+
+       /* Grab the inode. */
+       error = xfs_iget(mp, tp, bmap->me_owner, 0, XFS_ILOCK_EXCL, &ip);
+       if (error)
+               goto err_inode;
+
+       if (VFS_I(ip)->i_nlink == 0)
+               xfs_iflags_set(ip, XFS_IRECOVERY);
+       xfs_defer_init(&dfops, &firstfsb);
+
+       /* Process deferred bmap item. */
+       state = (bmap->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ?
+                       XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
+       whichfork = (bmap->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ?
+                       XFS_ATTR_FORK : XFS_DATA_FORK;
+       bui_type = bmap->me_flags & XFS_BMAP_EXTENT_TYPE_MASK;
+       switch (bui_type) {
+       case XFS_BMAP_MAP:
+       case XFS_BMAP_UNMAP:
+               type = bui_type;
+               break;
+       default:
+               error = -EFSCORRUPTED;
+               goto err_dfops;
+       }
+       xfs_trans_ijoin(tp, ip, 0);
+
+       error = xfs_trans_log_finish_bmap_update(tp, budp, &dfops, type,
+                       ip, whichfork, bmap->me_startoff,
+                       bmap->me_startblock, bmap->me_len,
+                       state);
+       if (error)
+               goto err_dfops;
+
+       /* Finish transaction, free inodes. */
+       error = xfs_defer_finish(&tp, &dfops, NULL);
+       if (error)
+               goto err_dfops;
+
+       set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);
+       error = xfs_trans_commit(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       IRELE(ip);
+
+       return error;
+
+err_dfops:
+       xfs_defer_cancel(&dfops);
+err_inode:
+       xfs_trans_cancel(tp);
+       if (ip) {
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               IRELE(ip);
+       }
+       return error;
+}
diff --git a/fs/xfs/xfs_bmap_item.h b/fs/xfs/xfs_bmap_item.h
new file mode 100644 (file)
index 0000000..c867daa
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef        __XFS_BMAP_ITEM_H__
+#define        __XFS_BMAP_ITEM_H__
+
+/*
+ * There are (currently) two pairs of bmap btree redo item types: map & unmap.
+ * The common abbreviations for these are BUI (bmap update intent) and BUD
+ * (bmap update done).  The redo item type is encoded in the flags field of
+ * each xfs_map_extent.
+ *
+ * *I items should be recorded in the *first* of a series of rolled
+ * transactions, and the *D items should be recorded in the same transaction
+ * that records the associated bmbt updates.
+ *
+ * Should the system crash after the commit of the first transaction but
+ * before the commit of the final transaction in a series, log recovery will
+ * use the redo information recorded by the intent items to replay the
+ * bmbt metadata updates in the non-first transaction.
+ */
+
+/* kernel only BUI/BUD definitions */
+
+struct xfs_mount;
+struct kmem_zone;
+
+/*
+ * Max number of extents in fast allocation path.
+ */
+#define        XFS_BUI_MAX_FAST_EXTENTS        1
+
+/*
+ * Define BUI flag bits. Manipulated by set/clear/test_bit operators.
+ */
+#define        XFS_BUI_RECOVERED               1
+
+/*
+ * This is the "bmap update intent" log item.  It is used to log the fact that
+ * some reverse mappings need to change.  It is used in conjunction with the
+ * "bmap update done" log item described below.
+ *
+ * These log items follow the same rules as struct xfs_efi_log_item; see the
+ * comments about that structure (in xfs_extfree_item.h) for more details.
+ */
+struct xfs_bui_log_item {
+       struct xfs_log_item             bui_item;
+       atomic_t                        bui_refcount;
+       atomic_t                        bui_next_extent;
+       unsigned long                   bui_flags;      /* misc flags */
+       struct xfs_bui_log_format       bui_format;
+};
+
+static inline size_t
+xfs_bui_log_item_sizeof(
+       unsigned int            nr)
+{
+       return offsetof(struct xfs_bui_log_item, bui_format) +
+                       xfs_bui_log_format_sizeof(nr);
+}
+
+/*
+ * This is the "bmap update done" log item.  It is used to log the fact that
+ * some bmbt updates mentioned in an earlier bui item have been performed.
+ */
+struct xfs_bud_log_item {
+       struct xfs_log_item             bud_item;
+       struct xfs_bui_log_item         *bud_buip;
+       struct xfs_bud_log_format       bud_format;
+};
+
+extern struct kmem_zone        *xfs_bui_zone;
+extern struct kmem_zone        *xfs_bud_zone;
+
+struct xfs_bui_log_item *xfs_bui_init(struct xfs_mount *);
+struct xfs_bud_log_item *xfs_bud_init(struct xfs_mount *,
+               struct xfs_bui_log_item *);
+void xfs_bui_item_free(struct xfs_bui_log_item *);
+void xfs_bui_release(struct xfs_bui_log_item *);
+int xfs_bui_recover(struct xfs_mount *mp, struct xfs_bui_log_item *buip);
+
+#endif /* __XFS_BMAP_ITEM_H__ */
index e827d657c3145cb65d90725a476ce719b2a6c2ac..552465e011ecb0df23aaeae57b94108dcdd24360 100644 (file)
@@ -42,6 +42,9 @@
 #include "xfs_icache.h"
 #include "xfs_log.h"
 #include "xfs_rmap_btree.h"
+#include "xfs_iomap.h"
+#include "xfs_reflink.h"
+#include "xfs_refcount.h"
 
 /* Kernel only BMAP related definitions and functions */
 
@@ -389,11 +392,13 @@ xfs_bmap_count_blocks(
 STATIC int
 xfs_getbmapx_fix_eof_hole(
        xfs_inode_t             *ip,            /* xfs incore inode pointer */
+       int                     whichfork,
        struct getbmapx         *out,           /* output structure */
        int                     prealloced,     /* this is a file with
                                                 * preallocated data space */
        __int64_t               end,            /* last block requested */
-       xfs_fsblock_t           startblock)
+       xfs_fsblock_t           startblock,
+       bool                    moretocome)
 {
        __int64_t               fixlen;
        xfs_mount_t             *mp;            /* file system mount point */
@@ -418,8 +423,9 @@ xfs_getbmapx_fix_eof_hole(
                else
                        out->bmv_block = xfs_fsb_to_db(ip, startblock);
                fileblock = XFS_BB_TO_FSB(ip->i_mount, out->bmv_offset);
-               ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
-               if (xfs_iext_bno_to_ext(ifp, fileblock, &lastx) &&
+               ifp = XFS_IFORK_PTR(ip, whichfork);
+               if (!moretocome &&
+                   xfs_iext_bno_to_ext(ifp, fileblock, &lastx) &&
                   (lastx == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))-1))
                        out->bmv_oflags |= BMV_OF_LAST;
        }
@@ -427,6 +433,81 @@ xfs_getbmapx_fix_eof_hole(
        return 1;
 }
 
+/* Adjust the reported bmap around shared/unshared extent transitions. */
+STATIC int
+xfs_getbmap_adjust_shared(
+       struct xfs_inode                *ip,
+       int                             whichfork,
+       struct xfs_bmbt_irec            *map,
+       struct getbmapx                 *out,
+       struct xfs_bmbt_irec            *next_map)
+{
+       struct xfs_mount                *mp = ip->i_mount;
+       xfs_agnumber_t                  agno;
+       xfs_agblock_t                   agbno;
+       xfs_agblock_t                   ebno;
+       xfs_extlen_t                    elen;
+       xfs_extlen_t                    nlen;
+       int                             error;
+
+       next_map->br_startblock = NULLFSBLOCK;
+       next_map->br_startoff = NULLFILEOFF;
+       next_map->br_blockcount = 0;
+
+       /* Only written data blocks can be shared. */
+       if (!xfs_is_reflink_inode(ip) || whichfork != XFS_DATA_FORK ||
+           map->br_startblock == DELAYSTARTBLOCK ||
+           map->br_startblock == HOLESTARTBLOCK ||
+           ISUNWRITTEN(map))
+               return 0;
+
+       agno = XFS_FSB_TO_AGNO(mp, map->br_startblock);
+       agbno = XFS_FSB_TO_AGBNO(mp, map->br_startblock);
+       error = xfs_reflink_find_shared(mp, agno, agbno, map->br_blockcount,
+                       &ebno, &elen, true);
+       if (error)
+               return error;
+
+       if (ebno == NULLAGBLOCK) {
+               /* No shared blocks at all. */
+               return 0;
+       } else if (agbno == ebno) {
+               /*
+                * Shared extent at (agbno, elen).  Shrink the reported
+                * extent length and prepare to move the start of map[i]
+                * to agbno+elen, with the aim of (re)formatting the new
+                * map[i] the next time through the inner loop.
+                */
+               out->bmv_length = XFS_FSB_TO_BB(mp, elen);
+               out->bmv_oflags |= BMV_OF_SHARED;
+               if (elen != map->br_blockcount) {
+                       *next_map = *map;
+                       next_map->br_startblock += elen;
+                       next_map->br_startoff += elen;
+                       next_map->br_blockcount -= elen;
+               }
+               map->br_blockcount -= elen;
+       } else {
+               /*
+                * There's an unshared extent (agbno, ebno - agbno)
+                * followed by shared extent at (ebno, elen).  Shrink
+                * the reported extent length to cover only the unshared
+                * extent and prepare to move up the start of map[i] to
+                * ebno, with the aim of (re)formatting the new map[i]
+                * the next time through the inner loop.
+                */
+               *next_map = *map;
+               nlen = ebno - agbno;
+               out->bmv_length = XFS_FSB_TO_BB(mp, nlen);
+               next_map->br_startblock += nlen;
+               next_map->br_startoff += nlen;
+               next_map->br_blockcount -= nlen;
+               map->br_blockcount -= nlen;
+       }
+
+       return 0;
+}
+
 /*
  * Get inode's extents as described in bmv, and format for output.
  * Calls formatter to fill the user's buffer until all extents
@@ -459,12 +540,28 @@ xfs_getbmap(
        int                     iflags;         /* interface flags */
        int                     bmapi_flags;    /* flags for xfs_bmapi */
        int                     cur_ext = 0;
+       struct xfs_bmbt_irec    inject_map;
 
        mp = ip->i_mount;
        iflags = bmv->bmv_iflags;
-       whichfork = iflags & BMV_IF_ATTRFORK ? XFS_ATTR_FORK : XFS_DATA_FORK;
 
-       if (whichfork == XFS_ATTR_FORK) {
+#ifndef DEBUG
+       /* Only allow CoW fork queries if we're debugging. */
+       if (iflags & BMV_IF_COWFORK)
+               return -EINVAL;
+#endif
+       if ((iflags & BMV_IF_ATTRFORK) && (iflags & BMV_IF_COWFORK))
+               return -EINVAL;
+
+       if (iflags & BMV_IF_ATTRFORK)
+               whichfork = XFS_ATTR_FORK;
+       else if (iflags & BMV_IF_COWFORK)
+               whichfork = XFS_COW_FORK;
+       else
+               whichfork = XFS_DATA_FORK;
+
+       switch (whichfork) {
+       case XFS_ATTR_FORK:
                if (XFS_IFORK_Q(ip)) {
                        if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS &&
                            ip->i_d.di_aformat != XFS_DINODE_FMT_BTREE &&
@@ -480,7 +577,20 @@ xfs_getbmap(
 
                prealloced = 0;
                fixlen = 1LL << 32;
-       } else {
+               break;
+       case XFS_COW_FORK:
+               if (ip->i_cformat != XFS_DINODE_FMT_EXTENTS)
+                       return -EINVAL;
+
+               if (xfs_get_cowextsz_hint(ip)) {
+                       prealloced = 1;
+                       fixlen = mp->m_super->s_maxbytes;
+               } else {
+                       prealloced = 0;
+                       fixlen = XFS_ISIZE(ip);
+               }
+               break;
+       default:
                if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
                    ip->i_d.di_format != XFS_DINODE_FMT_BTREE &&
                    ip->i_d.di_format != XFS_DINODE_FMT_LOCAL)
@@ -494,6 +604,7 @@ xfs_getbmap(
                        prealloced = 0;
                        fixlen = XFS_ISIZE(ip);
                }
+               break;
        }
 
        if (bmv->bmv_length == -1) {
@@ -520,7 +631,8 @@ xfs_getbmap(
                return -ENOMEM;
 
        xfs_ilock(ip, XFS_IOLOCK_SHARED);
-       if (whichfork == XFS_DATA_FORK) {
+       switch (whichfork) {
+       case XFS_DATA_FORK:
                if (!(iflags & BMV_IF_DELALLOC) &&
                    (ip->i_delayed_blks || XFS_ISIZE(ip) > ip->i_d.di_size)) {
                        error = filemap_write_and_wait(VFS_I(ip)->i_mapping);
@@ -538,8 +650,14 @@ xfs_getbmap(
                }
 
                lock = xfs_ilock_data_map_shared(ip);
-       } else {
+               break;
+       case XFS_COW_FORK:
+               lock = XFS_ILOCK_SHARED;
+               xfs_ilock(ip, lock);
+               break;
+       case XFS_ATTR_FORK:
                lock = xfs_ilock_attr_map_shared(ip);
+               break;
        }
 
        /*
@@ -581,7 +699,8 @@ xfs_getbmap(
                        goto out_free_map;
                ASSERT(nmap <= subnex);
 
-               for (i = 0; i < nmap && nexleft && bmv->bmv_length; i++) {
+               for (i = 0; i < nmap && nexleft && bmv->bmv_length &&
+                               cur_ext < bmv->bmv_count; i++) {
                        out[cur_ext].bmv_oflags = 0;
                        if (map[i].br_state == XFS_EXT_UNWRITTEN)
                                out[cur_ext].bmv_oflags |= BMV_OF_PREALLOC;
@@ -614,9 +733,16 @@ xfs_getbmap(
                                goto out_free_map;
                        }
 
-                       if (!xfs_getbmapx_fix_eof_hole(ip, &out[cur_ext],
-                                       prealloced, bmvend,
-                                       map[i].br_startblock))
+                       /* Is this a shared block? */
+                       error = xfs_getbmap_adjust_shared(ip, whichfork,
+                                       &map[i], &out[cur_ext], &inject_map);
+                       if (error)
+                               goto out_free_map;
+
+                       if (!xfs_getbmapx_fix_eof_hole(ip, whichfork,
+                                       &out[cur_ext], prealloced, bmvend,
+                                       map[i].br_startblock,
+                                       inject_map.br_startblock != NULLFSBLOCK))
                                goto out_free_map;
 
                        bmv->bmv_offset =
@@ -636,11 +762,16 @@ xfs_getbmap(
                                continue;
                        }
 
-                       nexleft--;
+                       if (inject_map.br_startblock != NULLFSBLOCK) {
+                               map[i] = inject_map;
+                               i--;
+                       } else
+                               nexleft--;
                        bmv->bmv_entries++;
                        cur_ext++;
                }
-       } while (nmap && nexleft && bmv->bmv_length);
+       } while (nmap && nexleft && bmv->bmv_length &&
+                cur_ext < bmv->bmv_count);
 
  out_free_map:
        kmem_free(map);
@@ -1433,8 +1564,8 @@ xfs_insert_file_space(
  */
 static int
 xfs_swap_extents_check_format(
-       xfs_inode_t     *ip,    /* target inode */
-       xfs_inode_t     *tip)   /* tmp inode */
+       struct xfs_inode        *ip,    /* target inode */
+       struct xfs_inode        *tip)   /* tmp inode */
 {
 
        /* Should never get a local format */
@@ -1449,6 +1580,13 @@ xfs_swap_extents_check_format(
        if (ip->i_d.di_nextents < tip->i_d.di_nextents)
                return -EINVAL;
 
+       /*
+        * If we have to use the (expensive) rmap swap method, we can
+        * handle any number of extents and any format.
+        */
+       if (xfs_sb_version_hasrmapbt(&ip->i_mount->m_sb))
+               return 0;
+
        /*
         * if the target inode is in extent form and the temp inode is in btree
         * form then we will end up with the target inode in the wrong format
@@ -1518,125 +1656,161 @@ xfs_swap_extent_flush(
        return 0;
 }
 
-int
-xfs_swap_extents(
-       xfs_inode_t     *ip,    /* target inode */
-       xfs_inode_t     *tip,   /* tmp inode */
-       xfs_swapext_t   *sxp)
+/*
+ * Move extents from one file to another, when rmap is enabled.
+ */
+STATIC int
+xfs_swap_extent_rmap(
+       struct xfs_trans                **tpp,
+       struct xfs_inode                *ip,
+       struct xfs_inode                *tip)
 {
-       xfs_mount_t     *mp = ip->i_mount;
-       xfs_trans_t     *tp;
-       xfs_bstat_t     *sbp = &sxp->sx_stat;
-       xfs_ifork_t     *tempifp, *ifp, *tifp;
-       int             src_log_flags, target_log_flags;
-       int             error = 0;
-       int             aforkblks = 0;
-       int             taforkblks = 0;
-       __uint64_t      tmp;
-       int             lock_flags;
-
-       /* XXX: we can't do this with rmap, will fix later */
-       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
-               return -EOPNOTSUPP;
-
-       tempifp = kmem_alloc(sizeof(xfs_ifork_t), KM_MAYFAIL);
-       if (!tempifp) {
-               error = -ENOMEM;
-               goto out;
-       }
+       struct xfs_bmbt_irec            irec;
+       struct xfs_bmbt_irec            uirec;
+       struct xfs_bmbt_irec            tirec;
+       xfs_fileoff_t                   offset_fsb;
+       xfs_fileoff_t                   end_fsb;
+       xfs_filblks_t                   count_fsb;
+       xfs_fsblock_t                   firstfsb;
+       struct xfs_defer_ops            dfops;
+       int                             error;
+       xfs_filblks_t                   ilen;
+       xfs_filblks_t                   rlen;
+       int                             nimaps;
+       __uint64_t                      tip_flags2;
 
        /*
-        * Lock the inodes against other IO, page faults and truncate to
-        * begin with.  Then we can ensure the inodes are flushed and have no
-        * page cache safely. Once we have done this we can take the ilocks and
-        * do the rest of the checks.
+        * If the source file has shared blocks, we must flag the donor
+        * file as having shared blocks so that we get the shared-block
+        * rmap functions when we go to fix up the rmaps.  The flags
+        * will be switch for reals later.
         */
-       lock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
-       xfs_lock_two_inodes(ip, tip, XFS_IOLOCK_EXCL);
-       xfs_lock_two_inodes(ip, tip, XFS_MMAPLOCK_EXCL);
-
-       /* Verify that both files have the same format */
-       if ((VFS_I(ip)->i_mode & S_IFMT) != (VFS_I(tip)->i_mode & S_IFMT)) {
-               error = -EINVAL;
-               goto out_unlock;
-       }
+       tip_flags2 = tip->i_d.di_flags2;
+       if (ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK)
+               tip->i_d.di_flags2 |= XFS_DIFLAG2_REFLINK;
+
+       offset_fsb = 0;
+       end_fsb = XFS_B_TO_FSB(ip->i_mount, i_size_read(VFS_I(ip)));
+       count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb);
+
+       while (count_fsb) {
+               /* Read extent from the donor file */
+               nimaps = 1;
+               error = xfs_bmapi_read(tip, offset_fsb, count_fsb, &tirec,
+                               &nimaps, 0);
+               if (error)
+                       goto out;
+               ASSERT(nimaps == 1);
+               ASSERT(tirec.br_startblock != DELAYSTARTBLOCK);
+
+               trace_xfs_swap_extent_rmap_remap(tip, &tirec);
+               ilen = tirec.br_blockcount;
+
+               /* Unmap the old blocks in the source file. */
+               while (tirec.br_blockcount) {
+                       xfs_defer_init(&dfops, &firstfsb);
+                       trace_xfs_swap_extent_rmap_remap_piece(tip, &tirec);
+
+                       /* Read extent from the source file */
+                       nimaps = 1;
+                       error = xfs_bmapi_read(ip, tirec.br_startoff,
+                                       tirec.br_blockcount, &irec,
+                                       &nimaps, 0);
+                       if (error)
+                               goto out_defer;
+                       ASSERT(nimaps == 1);
+                       ASSERT(tirec.br_startoff == irec.br_startoff);
+                       trace_xfs_swap_extent_rmap_remap_piece(ip, &irec);
+
+                       /* Trim the extent. */
+                       uirec = tirec;
+                       uirec.br_blockcount = rlen = min_t(xfs_filblks_t,
+                                       tirec.br_blockcount,
+                                       irec.br_blockcount);
+                       trace_xfs_swap_extent_rmap_remap_piece(tip, &uirec);
+
+                       /* Remove the mapping from the donor file. */
+                       error = xfs_bmap_unmap_extent((*tpp)->t_mountp, &dfops,
+                                       tip, &uirec);
+                       if (error)
+                               goto out_defer;
 
-       /* Verify both files are either real-time or non-realtime */
-       if (XFS_IS_REALTIME_INODE(ip) != XFS_IS_REALTIME_INODE(tip)) {
-               error = -EINVAL;
-               goto out_unlock;
-       }
+                       /* Remove the mapping from the source file. */
+                       error = xfs_bmap_unmap_extent((*tpp)->t_mountp, &dfops,
+                                       ip, &irec);
+                       if (error)
+                               goto out_defer;
 
-       error = xfs_swap_extent_flush(ip);
-       if (error)
-               goto out_unlock;
-       error = xfs_swap_extent_flush(tip);
-       if (error)
-               goto out_unlock;
+                       /* Map the donor file's blocks into the source file. */
+                       error = xfs_bmap_map_extent((*tpp)->t_mountp, &dfops,
+                                       ip, &uirec);
+                       if (error)
+                               goto out_defer;
 
-       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
-       if (error)
-               goto out_unlock;
+                       /* Map the source file's blocks into the donor file. */
+                       error = xfs_bmap_map_extent((*tpp)->t_mountp, &dfops,
+                                       tip, &irec);
+                       if (error)
+                               goto out_defer;
 
-       /*
-        * Lock and join the inodes to the tansaction so that transaction commit
-        * or cancel will unlock the inodes from this point onwards.
-        */
-       xfs_lock_two_inodes(ip, tip, XFS_ILOCK_EXCL);
-       lock_flags |= XFS_ILOCK_EXCL;
-       xfs_trans_ijoin(tp, ip, lock_flags);
-       xfs_trans_ijoin(tp, tip, lock_flags);
+                       error = xfs_defer_finish(tpp, &dfops, ip);
+                       if (error)
+                               goto out_defer;
 
+                       tirec.br_startoff += rlen;
+                       if (tirec.br_startblock != HOLESTARTBLOCK &&
+                           tirec.br_startblock != DELAYSTARTBLOCK)
+                               tirec.br_startblock += rlen;
+                       tirec.br_blockcount -= rlen;
+               }
 
-       /* Verify all data are being swapped */
-       if (sxp->sx_offset != 0 ||
-           sxp->sx_length != ip->i_d.di_size ||
-           sxp->sx_length != tip->i_d.di_size) {
-               error = -EFAULT;
-               goto out_trans_cancel;
+               /* Roll on... */
+               count_fsb -= ilen;
+               offset_fsb += ilen;
        }
 
-       trace_xfs_swap_extent_before(ip, 0);
-       trace_xfs_swap_extent_before(tip, 1);
+       tip->i_d.di_flags2 = tip_flags2;
+       return 0;
 
-       /* check inode formats now that data is flushed */
-       error = xfs_swap_extents_check_format(ip, tip);
-       if (error) {
-               xfs_notice(mp,
-                   "%s: inode 0x%llx format is incompatible for exchanging.",
-                               __func__, ip->i_ino);
-               goto out_trans_cancel;
-       }
+out_defer:
+       xfs_defer_cancel(&dfops);
+out:
+       trace_xfs_swap_extent_rmap_error(ip, error, _RET_IP_);
+       tip->i_d.di_flags2 = tip_flags2;
+       return error;
+}
+
+/* Swap the extents of two files by swapping data forks. */
+STATIC int
+xfs_swap_extent_forks(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip,
+       struct xfs_inode        *tip,
+       int                     *src_log_flags,
+       int                     *target_log_flags)
+{
+       struct xfs_ifork        tempifp, *ifp, *tifp;
+       int                     aforkblks = 0;
+       int                     taforkblks = 0;
+       __uint64_t              tmp;
+       int                     error;
 
-       /*
-        * Compare the current change & modify times with that
-        * passed in.  If they differ, we abort this swap.
-        * This is the mechanism used to ensure the calling
-        * process that the file was not changed out from
-        * under it.
-        */
-       if ((sbp->bs_ctime.tv_sec != VFS_I(ip)->i_ctime.tv_sec) ||
-           (sbp->bs_ctime.tv_nsec != VFS_I(ip)->i_ctime.tv_nsec) ||
-           (sbp->bs_mtime.tv_sec != VFS_I(ip)->i_mtime.tv_sec) ||
-           (sbp->bs_mtime.tv_nsec != VFS_I(ip)->i_mtime.tv_nsec)) {
-               error = -EBUSY;
-               goto out_trans_cancel;
-       }
        /*
         * Count the number of extended attribute blocks
         */
        if ( ((XFS_IFORK_Q(ip) != 0) && (ip->i_d.di_anextents > 0)) &&
             (ip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) {
-               error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &aforkblks);
+               error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK,
+                               &aforkblks);
                if (error)
-                       goto out_trans_cancel;
+                       return error;
        }
        if ( ((XFS_IFORK_Q(tip) != 0) && (tip->i_d.di_anextents > 0)) &&
             (tip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) {
                error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK,
-                       &taforkblks);
+                               &taforkblks);
                if (error)
-                       goto out_trans_cancel;
+                       return error;
        }
 
        /*
@@ -1645,31 +1819,23 @@ xfs_swap_extents(
         * buffers, and so the validation done on read will expect the owner
         * field to be correctly set. Once we change the owners, we can swap the
         * inode forks.
-        *
-        * Note the trickiness in setting the log flags - we set the owner log
-        * flag on the opposite inode (i.e. the inode we are setting the new
-        * owner to be) because once we swap the forks and log that, log
-        * recovery is going to see the fork as owned by the swapped inode,
-        * not the pre-swapped inodes.
         */
-       src_log_flags = XFS_ILOG_CORE;
-       target_log_flags = XFS_ILOG_CORE;
        if (ip->i_d.di_version == 3 &&
            ip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
-               target_log_flags |= XFS_ILOG_DOWNER;
+               (*target_log_flags) |= XFS_ILOG_DOWNER;
                error = xfs_bmbt_change_owner(tp, ip, XFS_DATA_FORK,
                                              tip->i_ino, NULL);
                if (error)
-                       goto out_trans_cancel;
+                       return error;
        }
 
        if (tip->i_d.di_version == 3 &&
            tip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
-               src_log_flags |= XFS_ILOG_DOWNER;
+               (*src_log_flags) |= XFS_ILOG_DOWNER;
                error = xfs_bmbt_change_owner(tp, tip, XFS_DATA_FORK,
                                              ip->i_ino, NULL);
                if (error)
-                       goto out_trans_cancel;
+                       return error;
        }
 
        /*
@@ -1677,9 +1843,9 @@ xfs_swap_extents(
         */
        ifp = &ip->i_df;
        tifp = &tip->i_df;
-       *tempifp = *ifp;        /* struct copy */
+       tempifp = *ifp;         /* struct copy */
        *ifp = *tifp;           /* struct copy */
-       *tifp = *tempifp;       /* struct copy */
+       *tifp = tempifp;        /* struct copy */
 
        /*
         * Fix the on-disk inode values
@@ -1719,12 +1885,12 @@ xfs_swap_extents(
                        ifp->if_u1.if_extents =
                                ifp->if_u2.if_inline_ext;
                }
-               src_log_flags |= XFS_ILOG_DEXT;
+               (*src_log_flags) |= XFS_ILOG_DEXT;
                break;
        case XFS_DINODE_FMT_BTREE:
                ASSERT(ip->i_d.di_version < 3 ||
-                      (src_log_flags & XFS_ILOG_DOWNER));
-               src_log_flags |= XFS_ILOG_DBROOT;
+                      (*src_log_flags & XFS_ILOG_DOWNER));
+               (*src_log_flags) |= XFS_ILOG_DBROOT;
                break;
        }
 
@@ -1738,15 +1904,166 @@ xfs_swap_extents(
                        tifp->if_u1.if_extents =
                                tifp->if_u2.if_inline_ext;
                }
-               target_log_flags |= XFS_ILOG_DEXT;
+               (*target_log_flags) |= XFS_ILOG_DEXT;
                break;
        case XFS_DINODE_FMT_BTREE:
-               target_log_flags |= XFS_ILOG_DBROOT;
+               (*target_log_flags) |= XFS_ILOG_DBROOT;
                ASSERT(tip->i_d.di_version < 3 ||
-                      (target_log_flags & XFS_ILOG_DOWNER));
+                      (*target_log_flags & XFS_ILOG_DOWNER));
                break;
        }
 
+       return 0;
+}
+
+int
+xfs_swap_extents(
+       struct xfs_inode        *ip,    /* target inode */
+       struct xfs_inode        *tip,   /* tmp inode */
+       struct xfs_swapext      *sxp)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       struct xfs_bstat        *sbp = &sxp->sx_stat;
+       int                     src_log_flags, target_log_flags;
+       int                     error = 0;
+       int                     lock_flags;
+       struct xfs_ifork        *cowfp;
+       __uint64_t              f;
+       int                     resblks;
+
+       /*
+        * Lock the inodes against other IO, page faults and truncate to
+        * begin with.  Then we can ensure the inodes are flushed and have no
+        * page cache safely. Once we have done this we can take the ilocks and
+        * do the rest of the checks.
+        */
+       lock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
+       xfs_lock_two_inodes(ip, tip, XFS_IOLOCK_EXCL);
+       xfs_lock_two_inodes(ip, tip, XFS_MMAPLOCK_EXCL);
+
+       /* Verify that both files have the same format */
+       if ((VFS_I(ip)->i_mode & S_IFMT) != (VFS_I(tip)->i_mode & S_IFMT)) {
+               error = -EINVAL;
+               goto out_unlock;
+       }
+
+       /* Verify both files are either real-time or non-realtime */
+       if (XFS_IS_REALTIME_INODE(ip) != XFS_IS_REALTIME_INODE(tip)) {
+               error = -EINVAL;
+               goto out_unlock;
+       }
+
+       error = xfs_swap_extent_flush(ip);
+       if (error)
+               goto out_unlock;
+       error = xfs_swap_extent_flush(tip);
+       if (error)
+               goto out_unlock;
+
+       /*
+        * Extent "swapping" with rmap requires a permanent reservation and
+        * a block reservation because it's really just a remap operation
+        * performed with log redo items!
+        */
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+               /*
+                * Conceptually this shouldn't affect the shape of either
+                * bmbt, but since we atomically move extents one by one,
+                * we reserve enough space to rebuild both trees.
+                */
+               resblks = XFS_SWAP_RMAP_SPACE_RES(mp,
+                               XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK),
+                               XFS_DATA_FORK) +
+                         XFS_SWAP_RMAP_SPACE_RES(mp,
+                               XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK),
+                               XFS_DATA_FORK);
+               error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks,
+                               0, 0, &tp);
+       } else
+               error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0,
+                               0, 0, &tp);
+       if (error)
+               goto out_unlock;
+
+       /*
+        * Lock and join the inodes to the tansaction so that transaction commit
+        * or cancel will unlock the inodes from this point onwards.
+        */
+       xfs_lock_two_inodes(ip, tip, XFS_ILOCK_EXCL);
+       lock_flags |= XFS_ILOCK_EXCL;
+       xfs_trans_ijoin(tp, ip, 0);
+       xfs_trans_ijoin(tp, tip, 0);
+
+
+       /* Verify all data are being swapped */
+       if (sxp->sx_offset != 0 ||
+           sxp->sx_length != ip->i_d.di_size ||
+           sxp->sx_length != tip->i_d.di_size) {
+               error = -EFAULT;
+               goto out_trans_cancel;
+       }
+
+       trace_xfs_swap_extent_before(ip, 0);
+       trace_xfs_swap_extent_before(tip, 1);
+
+       /* check inode formats now that data is flushed */
+       error = xfs_swap_extents_check_format(ip, tip);
+       if (error) {
+               xfs_notice(mp,
+                   "%s: inode 0x%llx format is incompatible for exchanging.",
+                               __func__, ip->i_ino);
+               goto out_trans_cancel;
+       }
+
+       /*
+        * Compare the current change & modify times with that
+        * passed in.  If they differ, we abort this swap.
+        * This is the mechanism used to ensure the calling
+        * process that the file was not changed out from
+        * under it.
+        */
+       if ((sbp->bs_ctime.tv_sec != VFS_I(ip)->i_ctime.tv_sec) ||
+           (sbp->bs_ctime.tv_nsec != VFS_I(ip)->i_ctime.tv_nsec) ||
+           (sbp->bs_mtime.tv_sec != VFS_I(ip)->i_mtime.tv_sec) ||
+           (sbp->bs_mtime.tv_nsec != VFS_I(ip)->i_mtime.tv_nsec)) {
+               error = -EBUSY;
+               goto out_trans_cancel;
+       }
+
+       /*
+        * Note the trickiness in setting the log flags - we set the owner log
+        * flag on the opposite inode (i.e. the inode we are setting the new
+        * owner to be) because once we swap the forks and log that, log
+        * recovery is going to see the fork as owned by the swapped inode,
+        * not the pre-swapped inodes.
+        */
+       src_log_flags = XFS_ILOG_CORE;
+       target_log_flags = XFS_ILOG_CORE;
+
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+               error = xfs_swap_extent_rmap(&tp, ip, tip);
+       else
+               error = xfs_swap_extent_forks(tp, ip, tip, &src_log_flags,
+                               &target_log_flags);
+       if (error)
+               goto out_trans_cancel;
+
+       /* Do we have to swap reflink flags? */
+       if ((ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK) ^
+           (tip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK)) {
+               f = ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK;
+               ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+               ip->i_d.di_flags2 |= tip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK;
+               tip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+               tip->i_d.di_flags2 |= f & XFS_DIFLAG2_REFLINK;
+               cowfp = ip->i_cowfp;
+               ip->i_cowfp = tip->i_cowfp;
+               tip->i_cowfp = cowfp;
+               xfs_inode_set_cowblocks_tag(ip);
+               xfs_inode_set_cowblocks_tag(tip);
+       }
+
        xfs_trans_log_inode(tp, ip,  src_log_flags);
        xfs_trans_log_inode(tp, tip, target_log_flags);
 
@@ -1761,16 +2078,16 @@ xfs_swap_extents(
 
        trace_xfs_swap_extent_after(ip, 0);
        trace_xfs_swap_extent_after(tip, 1);
-out:
-       kmem_free(tempifp);
-       return error;
 
-out_unlock:
        xfs_iunlock(ip, lock_flags);
        xfs_iunlock(tip, lock_flags);
-       goto out;
+       return error;
 
 out_trans_cancel:
        xfs_trans_cancel(tp);
-       goto out;
+
+out_unlock:
+       xfs_iunlock(ip, lock_flags);
+       xfs_iunlock(tip, lock_flags);
+       return error;
 }
index f44f7999697860ace70a6de316f81e76dee5bc08..29816981b50a92c1657b19778fd9e09f1d199c16 100644 (file)
@@ -84,7 +84,8 @@ xfs_dir2_sf_getdents(
 
        sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
 
-       ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count));
+       if (dp->i_d.di_size < xfs_dir2_sf_hdr_size(sfp->i8count))
+               return -EFSCORRUPTED;
 
        /*
         * If the block number in the offset is out of range, we're done.
index 3d224702fbc0c4f6469d1a475ffbbc70beadaead..05f8666733a09d3c6b23033d0f297d7491828d66 100644 (file)
@@ -92,7 +92,11 @@ extern void xfs_verifier_error(struct xfs_buf *bp);
 #define XFS_ERRTAG_BMAPIFORMAT                         21
 #define XFS_ERRTAG_FREE_EXTENT                         22
 #define XFS_ERRTAG_RMAP_FINISH_ONE                     23
-#define XFS_ERRTAG_MAX                                 24
+#define XFS_ERRTAG_REFCOUNT_CONTINUE_UPDATE            24
+#define XFS_ERRTAG_REFCOUNT_FINISH_ONE                 25
+#define XFS_ERRTAG_BMAP_FINISH_ONE                     26
+#define XFS_ERRTAG_AG_RESV_CRITICAL                    27
+#define XFS_ERRTAG_MAX                                 28
 
 /*
  * Random factors for above tags, 1 means always, 2 means 1/2 time, etc.
@@ -121,6 +125,10 @@ extern void xfs_verifier_error(struct xfs_buf *bp);
 #define        XFS_RANDOM_BMAPIFORMAT                          XFS_RANDOM_DEFAULT
 #define XFS_RANDOM_FREE_EXTENT                         1
 #define XFS_RANDOM_RMAP_FINISH_ONE                     1
+#define XFS_RANDOM_REFCOUNT_CONTINUE_UPDATE            1
+#define XFS_RANDOM_REFCOUNT_FINISH_ONE                 1
+#define XFS_RANDOM_BMAP_FINISH_ONE                     1
+#define XFS_RANDOM_AG_RESV_CRITICAL                    4
 
 #ifdef DEBUG
 extern int xfs_error_test_active;
index 2bc58b3fd37d27b1e6f0c11704a1cd7ae9710b18..a314fc7b56fa5b0fcfb0e234a9a931eda6de7e73 100644 (file)
@@ -38,6 +38,7 @@
 #include "xfs_icache.h"
 #include "xfs_pnfs.h"
 #include "xfs_iomap.h"
+#include "xfs_reflink.h"
 
 #include <linux/dcache.h>
 #include <linux/falloc.h>
@@ -634,6 +635,13 @@ xfs_file_dio_aio_write(
 
        trace_xfs_file_direct_write(ip, count, iocb->ki_pos);
 
+       /* If this is a block-aligned directio CoW, remap immediately. */
+       if (xfs_is_reflink_inode(ip) && !unaligned_io) {
+               ret = xfs_reflink_allocate_cow_range(ip, iocb->ki_pos, count);
+               if (ret)
+                       goto out;
+       }
+
        data = *from;
        ret = __blockdev_direct_IO(iocb, inode, target->bt_bdev, &data,
                        xfs_get_blocks_direct, xfs_end_io_direct_write,
@@ -735,6 +743,9 @@ write_retry:
                enospc = xfs_inode_free_quota_eofblocks(ip);
                if (enospc)
                        goto write_retry;
+               enospc = xfs_inode_free_quota_cowblocks(ip);
+               if (enospc)
+                       goto write_retry;
        } else if (ret == -ENOSPC && !enospc) {
                struct xfs_eofblocks eofb = {0};
 
@@ -774,10 +785,20 @@ xfs_file_write_iter(
 
        if (IS_DAX(inode))
                ret = xfs_file_dax_write(iocb, from);
-       else if (iocb->ki_flags & IOCB_DIRECT)
+       else if (iocb->ki_flags & IOCB_DIRECT) {
+               /*
+                * Allow a directio write to fall back to a buffered
+                * write *only* in the case that we're doing a reflink
+                * CoW.  In all other directio scenarios we do not
+                * allow an operation to fall back to buffered mode.
+                */
                ret = xfs_file_dio_aio_write(iocb, from);
-       else
+               if (ret == -EREMCHG)
+                       goto buffered;
+       } else {
+buffered:
                ret = xfs_file_buffered_aio_write(iocb, from);
+       }
 
        if (ret > 0) {
                XFS_STATS_ADD(ip->i_mount, xs_write_bytes, ret);
@@ -791,7 +812,7 @@ xfs_file_write_iter(
 #define        XFS_FALLOC_FL_SUPPORTED                                         \
                (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |           \
                 FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |      \
-                FALLOC_FL_INSERT_RANGE)
+                FALLOC_FL_INSERT_RANGE | FALLOC_FL_UNSHARE_RANGE)
 
 STATIC long
 xfs_file_fallocate(
@@ -881,9 +902,15 @@ xfs_file_fallocate(
 
                if (mode & FALLOC_FL_ZERO_RANGE)
                        error = xfs_zero_file_space(ip, offset, len);
-               else
+               else {
+                       if (mode & FALLOC_FL_UNSHARE_RANGE) {
+                               error = xfs_reflink_unshare(ip, offset, len);
+                               if (error)
+                                       goto out_unlock;
+                       }
                        error = xfs_alloc_file_space(ip, offset, len,
                                                     XFS_BMAPI_PREALLOC);
+               }
                if (error)
                        goto out_unlock;
        }
@@ -920,6 +947,189 @@ out_unlock:
        return error;
 }
 
+/*
+ * Flush all file writes out to disk.
+ */
+static int
+xfs_file_wait_for_io(
+       struct inode    *inode,
+       loff_t          offset,
+       size_t          len)
+{
+       loff_t          rounding;
+       loff_t          ioffset;
+       loff_t          iendoffset;
+       loff_t          bs;
+       int             ret;
+
+       bs = inode->i_sb->s_blocksize;
+       inode_dio_wait(inode);
+
+       rounding = max_t(xfs_off_t, bs, PAGE_SIZE);
+       ioffset = round_down(offset, rounding);
+       iendoffset = round_up(offset + len, rounding) - 1;
+       ret = filemap_write_and_wait_range(inode->i_mapping, ioffset,
+                                          iendoffset);
+       return ret;
+}
+
+/* Hook up to the VFS reflink function */
+STATIC int
+xfs_file_share_range(
+       struct file     *file_in,
+       loff_t          pos_in,
+       struct file     *file_out,
+       loff_t          pos_out,
+       u64             len,
+       bool            is_dedupe)
+{
+       struct inode    *inode_in;
+       struct inode    *inode_out;
+       ssize_t         ret;
+       loff_t          bs;
+       loff_t          isize;
+       int             same_inode;
+       loff_t          blen;
+       unsigned int    flags = 0;
+
+       inode_in = file_inode(file_in);
+       inode_out = file_inode(file_out);
+       bs = inode_out->i_sb->s_blocksize;
+
+       /* Don't touch certain kinds of inodes */
+       if (IS_IMMUTABLE(inode_out))
+               return -EPERM;
+       if (IS_SWAPFILE(inode_in) ||
+           IS_SWAPFILE(inode_out))
+               return -ETXTBSY;
+
+       /* Reflink only works within this filesystem. */
+       if (inode_in->i_sb != inode_out->i_sb)
+               return -EXDEV;
+       same_inode = (inode_in->i_ino == inode_out->i_ino);
+
+       /* Don't reflink dirs, pipes, sockets... */
+       if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
+               return -EISDIR;
+       if (S_ISFIFO(inode_in->i_mode) || S_ISFIFO(inode_out->i_mode))
+               return -EINVAL;
+       if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
+               return -EINVAL;
+
+       /* Don't share DAX file data for now. */
+       if (IS_DAX(inode_in) || IS_DAX(inode_out))
+               return -EINVAL;
+
+       /* Are we going all the way to the end? */
+       isize = i_size_read(inode_in);
+       if (isize == 0)
+               return 0;
+       if (len == 0)
+               len = isize - pos_in;
+
+       /* Ensure offsets don't wrap and the input is inside i_size */
+       if (pos_in + len < pos_in || pos_out + len < pos_out ||
+           pos_in + len > isize)
+               return -EINVAL;
+
+       /* Don't allow dedupe past EOF in the dest file */
+       if (is_dedupe) {
+               loff_t  disize;
+
+               disize = i_size_read(inode_out);
+               if (pos_out >= disize || pos_out + len > disize)
+                       return -EINVAL;
+       }
+
+       /* If we're linking to EOF, continue to the block boundary. */
+       if (pos_in + len == isize)
+               blen = ALIGN(isize, bs) - pos_in;
+       else
+               blen = len;
+
+       /* Only reflink if we're aligned to block boundaries */
+       if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_in + blen, bs) ||
+           !IS_ALIGNED(pos_out, bs) || !IS_ALIGNED(pos_out + blen, bs))
+               return -EINVAL;
+
+       /* Don't allow overlapped reflink within the same file */
+       if (same_inode && pos_out + blen > pos_in && pos_out < pos_in + blen)
+               return -EINVAL;
+
+       /* Wait for the completion of any pending IOs on srcfile */
+       ret = xfs_file_wait_for_io(inode_in, pos_in, len);
+       if (ret)
+               goto out;
+       ret = xfs_file_wait_for_io(inode_out, pos_out, len);
+       if (ret)
+               goto out;
+
+       if (is_dedupe)
+               flags |= XFS_REFLINK_DEDUPE;
+       ret = xfs_reflink_remap_range(XFS_I(inode_in), pos_in, XFS_I(inode_out),
+                       pos_out, len, flags);
+       if (ret < 0)
+               goto out;
+
+out:
+       return ret;
+}
+
+STATIC ssize_t
+xfs_file_copy_range(
+       struct file     *file_in,
+       loff_t          pos_in,
+       struct file     *file_out,
+       loff_t          pos_out,
+       size_t          len,
+       unsigned int    flags)
+{
+       int             error;
+
+       error = xfs_file_share_range(file_in, pos_in, file_out, pos_out,
+                                    len, false);
+       if (error)
+               return error;
+       return len;
+}
+
+STATIC int
+xfs_file_clone_range(
+       struct file     *file_in,
+       loff_t          pos_in,
+       struct file     *file_out,
+       loff_t          pos_out,
+       u64             len)
+{
+       return xfs_file_share_range(file_in, pos_in, file_out, pos_out,
+                                    len, false);
+}
+
+#define XFS_MAX_DEDUPE_LEN     (16 * 1024 * 1024)
+STATIC ssize_t
+xfs_file_dedupe_range(
+       struct file     *src_file,
+       u64             loff,
+       u64             len,
+       struct file     *dst_file,
+       u64             dst_loff)
+{
+       int             error;
+
+       /*
+        * Limit the total length we will dedupe for each operation.
+        * This is intended to bound the total time spent in this
+        * ioctl to something sane.
+        */
+       if (len > XFS_MAX_DEDUPE_LEN)
+               len = XFS_MAX_DEDUPE_LEN;
+
+       error = xfs_file_share_range(src_file, loff, dst_file, dst_loff,
+                                    len, true);
+       if (error)
+               return error;
+       return len;
+}
 
 STATIC int
 xfs_file_open(
@@ -1581,6 +1791,9 @@ const struct file_operations xfs_file_operations = {
        .fsync          = xfs_file_fsync,
        .get_unmapped_area = thp_get_unmapped_area,
        .fallocate      = xfs_file_fallocate,
+       .copy_file_range = xfs_file_copy_range,
+       .clone_file_range = xfs_file_clone_range,
+       .dedupe_file_range = xfs_file_dedupe_range,
 };
 
 const struct file_operations xfs_dir_file_operations = {
index 94ac06f3d908bf6ad12fa036bbc91e3d28571ab6..93d12fa2670d53bf8b6bf23c51325d48467b8ab2 100644 (file)
@@ -43,6 +43,7 @@
 #include "xfs_log.h"
 #include "xfs_filestream.h"
 #include "xfs_rmap.h"
+#include "xfs_ag_resv.h"
 
 /*
  * File system operations
@@ -108,7 +109,9 @@ xfs_fs_geometry(
                        (xfs_sb_version_hassparseinodes(&mp->m_sb) ?
                                XFS_FSOP_GEOM_FLAGS_SPINODES : 0) |
                        (xfs_sb_version_hasrmapbt(&mp->m_sb) ?
-                               XFS_FSOP_GEOM_FLAGS_RMAPBT : 0);
+                               XFS_FSOP_GEOM_FLAGS_RMAPBT : 0) |
+                       (xfs_sb_version_hasreflink(&mp->m_sb) ?
+                               XFS_FSOP_GEOM_FLAGS_REFLINK : 0);
                geo->logsectsize = xfs_sb_version_hassector(&mp->m_sb) ?
                                mp->m_sb.sb_logsectsize : BBSIZE;
                geo->rtsectsize = mp->m_sb.sb_blocksize;
@@ -259,6 +262,12 @@ xfs_growfs_data_private(
                agf->agf_longest = cpu_to_be32(tmpsize);
                if (xfs_sb_version_hascrc(&mp->m_sb))
                        uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid);
+               if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+                       agf->agf_refcount_root = cpu_to_be32(
+                                       xfs_refc_block(mp));
+                       agf->agf_refcount_level = cpu_to_be32(1);
+                       agf->agf_refcount_blocks = cpu_to_be32(1);
+               }
 
                error = xfs_bwrite(bp);
                xfs_buf_relse(bp);
@@ -450,6 +459,17 @@ xfs_growfs_data_private(
                        rrec->rm_offset = 0;
                        be16_add_cpu(&block->bb_numrecs, 1);
 
+                       /* account for refc btree root */
+                       if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+                               rrec = XFS_RMAP_REC_ADDR(block, 5);
+                               rrec->rm_startblock = cpu_to_be32(
+                                               xfs_refc_block(mp));
+                               rrec->rm_blockcount = cpu_to_be32(1);
+                               rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
+                               rrec->rm_offset = 0;
+                               be16_add_cpu(&block->bb_numrecs, 1);
+                       }
+
                        error = xfs_bwrite(bp);
                        xfs_buf_relse(bp);
                        if (error)
@@ -507,6 +527,28 @@ xfs_growfs_data_private(
                                goto error0;
                }
 
+               /*
+                * refcount btree root block
+                */
+               if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+                       bp = xfs_growfs_get_hdr_buf(mp,
+                               XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
+                               BTOBB(mp->m_sb.sb_blocksize), 0,
+                               &xfs_refcountbt_buf_ops);
+                       if (!bp) {
+                               error = -ENOMEM;
+                               goto error0;
+                       }
+
+                       xfs_btree_init_block(mp, bp, XFS_REFC_CRC_MAGIC,
+                                            0, 0, agno,
+                                            XFS_BTREE_CRC_BLOCKS);
+
+                       error = xfs_bwrite(bp);
+                       xfs_buf_relse(bp);
+                       if (error)
+                               goto error0;
+               }
        }
        xfs_trans_agblocks_delta(tp, nfree);
        /*
@@ -589,6 +631,11 @@ xfs_growfs_data_private(
        xfs_set_low_space_thresholds(mp);
        mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
 
+       /* Reserve AG metadata blocks. */
+       error = xfs_fs_reserve_ag_blocks(mp);
+       if (error && error != -ENOSPC)
+               goto out;
+
        /* update secondary superblocks. */
        for (agno = 1; agno < nagcount; agno++) {
                error = 0;
@@ -639,6 +686,8 @@ xfs_growfs_data_private(
                        continue;
                }
        }
+
+ out:
        return saved_error ? saved_error : error;
 
  error0:
@@ -948,3 +997,59 @@ xfs_do_force_shutdown(
        "Please umount the filesystem and rectify the problem(s)");
        }
 }
+
+/*
+ * Reserve free space for per-AG metadata.
+ */
+int
+xfs_fs_reserve_ag_blocks(
+       struct xfs_mount        *mp)
+{
+       xfs_agnumber_t          agno;
+       struct xfs_perag        *pag;
+       int                     error = 0;
+       int                     err2;
+
+       for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+               pag = xfs_perag_get(mp, agno);
+               err2 = xfs_ag_resv_init(pag);
+               xfs_perag_put(pag);
+               if (err2 && !error)
+                       error = err2;
+       }
+
+       if (error && error != -ENOSPC) {
+               xfs_warn(mp,
+       "Error %d reserving per-AG metadata reserve pool.", error);
+               xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+       }
+
+       return error;
+}
+
+/*
+ * Free space reserved for per-AG metadata.
+ */
+int
+xfs_fs_unreserve_ag_blocks(
+       struct xfs_mount        *mp)
+{
+       xfs_agnumber_t          agno;
+       struct xfs_perag        *pag;
+       int                     error = 0;
+       int                     err2;
+
+       for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+               pag = xfs_perag_get(mp, agno);
+               err2 = xfs_ag_resv_free(pag);
+               xfs_perag_put(pag);
+               if (err2 && !error)
+                       error = err2;
+       }
+
+       if (error)
+               xfs_warn(mp,
+       "Error %d freeing per-AG metadata reserve pool.", error);
+
+       return error;
+}
index f32713f14f9a21c1b752e2e8eb889dea72411f8e..f34915898fea25376099af70fd417442af171815 100644 (file)
@@ -26,4 +26,7 @@ extern int xfs_reserve_blocks(xfs_mount_t *mp, __uint64_t *inval,
                                xfs_fsop_resblks_t *outval);
 extern int xfs_fs_goingdown(xfs_mount_t *mp, __uint32_t inflags);
 
+extern int xfs_fs_reserve_ag_blocks(struct xfs_mount *mp);
+extern int xfs_fs_unreserve_ag_blocks(struct xfs_mount *mp);
+
 #endif /* __XFS_FSOPS_H__ */
index 4d41b241298fba5031fb93e59e39e209bf36b308..687a4b01fc53e842e6a686895631b5f0d159f3f3 100644 (file)
@@ -21,8 +21,8 @@
 /*
  * Tunable XFS parameters.  xfs_params is required even when CONFIG_SYSCTL=n,
  * other XFS code uses these values.  Times are measured in centisecs (i.e.
- * 100ths of a second) with the exception of eofb_timer, which is measured in
- * seconds.
+ * 100ths of a second) with the exception of eofb_timer and cowb_timer, which
+ * are measured in seconds.
  */
 xfs_param_t xfs_params = {
                          /*    MIN             DFLT            MAX     */
@@ -42,6 +42,7 @@ xfs_param_t xfs_params = {
        .inherit_nodfrg = {     0,              1,              1       },
        .fstrm_timer    = {     1,              30*100,         3600*100},
        .eofb_timer     = {     1,              300,            3600*24},
+       .cowb_timer     = {     1,              1800,           3600*24},
 };
 
 struct xfs_globals xfs_globals = {
index 65b2e3f85f52ac0b0851b63525afa8a6386bf80e..14796b744e0a1ebb9f8d428131d2522316262e82 100644 (file)
@@ -33,6 +33,7 @@
 #include "xfs_bmap_util.h"
 #include "xfs_dquot_item.h"
 #include "xfs_dquot.h"
+#include "xfs_reflink.h"
 
 #include <linux/kthread.h>
 #include <linux/freezer.h>
@@ -76,6 +77,9 @@ xfs_inode_alloc(
        ip->i_mount = mp;
        memset(&ip->i_imap, 0, sizeof(struct xfs_imap));
        ip->i_afp = NULL;
+       ip->i_cowfp = NULL;
+       ip->i_cnextents = 0;
+       ip->i_cformat = XFS_DINODE_FMT_EXTENTS;
        memset(&ip->i_df, 0, sizeof(xfs_ifork_t));
        ip->i_flags = 0;
        ip->i_delayed_blks = 0;
@@ -101,6 +105,8 @@ xfs_inode_free_callback(
 
        if (ip->i_afp)
                xfs_idestroy_fork(ip, XFS_ATTR_FORK);
+       if (ip->i_cowfp)
+               xfs_idestroy_fork(ip, XFS_COW_FORK);
 
        if (ip->i_itemp) {
                ASSERT(!(ip->i_itemp->ili_item.li_flags & XFS_LI_IN_AIL));
@@ -787,6 +793,33 @@ xfs_eofblocks_worker(
        xfs_queue_eofblocks(mp);
 }
 
+/*
+ * Background scanning to trim preallocated CoW space. This is queued
+ * based on the 'speculative_cow_prealloc_lifetime' tunable (5m by default).
+ * (We'll just piggyback on the post-EOF prealloc space workqueue.)
+ */
+STATIC void
+xfs_queue_cowblocks(
+       struct xfs_mount *mp)
+{
+       rcu_read_lock();
+       if (radix_tree_tagged(&mp->m_perag_tree, XFS_ICI_COWBLOCKS_TAG))
+               queue_delayed_work(mp->m_eofblocks_workqueue,
+                                  &mp->m_cowblocks_work,
+                                  msecs_to_jiffies(xfs_cowb_secs * 1000));
+       rcu_read_unlock();
+}
+
+void
+xfs_cowblocks_worker(
+       struct work_struct *work)
+{
+       struct xfs_mount *mp = container_of(to_delayed_work(work),
+                               struct xfs_mount, m_cowblocks_work);
+       xfs_icache_free_cowblocks(mp, NULL);
+       xfs_queue_cowblocks(mp);
+}
+
 int
 xfs_inode_ag_iterator(
        struct xfs_mount        *mp,
@@ -1343,18 +1376,30 @@ xfs_inode_free_eofblocks(
        return ret;
 }
 
-int
-xfs_icache_free_eofblocks(
+static int
+__xfs_icache_free_eofblocks(
        struct xfs_mount        *mp,
-       struct xfs_eofblocks    *eofb)
+       struct xfs_eofblocks    *eofb,
+       int                     (*execute)(struct xfs_inode *ip, int flags,
+                                          void *args),
+       int                     tag)
 {
        int flags = SYNC_TRYLOCK;
 
        if (eofb && (eofb->eof_flags & XFS_EOF_FLAGS_SYNC))
                flags = SYNC_WAIT;
 
-       return xfs_inode_ag_iterator_tag(mp, xfs_inode_free_eofblocks, flags,
-                                        eofb, XFS_ICI_EOFBLOCKS_TAG);
+       return xfs_inode_ag_iterator_tag(mp, execute, flags,
+                                        eofb, tag);
+}
+
+int
+xfs_icache_free_eofblocks(
+       struct xfs_mount        *mp,
+       struct xfs_eofblocks    *eofb)
+{
+       return __xfs_icache_free_eofblocks(mp, eofb, xfs_inode_free_eofblocks,
+                       XFS_ICI_EOFBLOCKS_TAG);
 }
 
 /*
@@ -1363,9 +1408,11 @@ xfs_icache_free_eofblocks(
  * failure. We make a best effort by including each quota under low free space
  * conditions (less than 1% free space) in the scan.
  */
-int
-xfs_inode_free_quota_eofblocks(
-       struct xfs_inode *ip)
+static int
+__xfs_inode_free_quota_eofblocks(
+       struct xfs_inode        *ip,
+       int                     (*execute)(struct xfs_mount *mp,
+                                          struct xfs_eofblocks *eofb))
 {
        int scan = 0;
        struct xfs_eofblocks eofb = {0};
@@ -1401,14 +1448,25 @@ xfs_inode_free_quota_eofblocks(
        }
 
        if (scan)
-               xfs_icache_free_eofblocks(ip->i_mount, &eofb);
+               execute(ip->i_mount, &eofb);
 
        return scan;
 }
 
-void
-xfs_inode_set_eofblocks_tag(
-       xfs_inode_t     *ip)
+int
+xfs_inode_free_quota_eofblocks(
+       struct xfs_inode *ip)
+{
+       return __xfs_inode_free_quota_eofblocks(ip, xfs_icache_free_eofblocks);
+}
+
+static void
+__xfs_inode_set_eofblocks_tag(
+       xfs_inode_t     *ip,
+       void            (*execute)(struct xfs_mount *mp),
+       void            (*set_tp)(struct xfs_mount *mp, xfs_agnumber_t agno,
+                                 int error, unsigned long caller_ip),
+       int             tag)
 {
        struct xfs_mount *mp = ip->i_mount;
        struct xfs_perag *pag;
@@ -1426,26 +1484,22 @@ xfs_inode_set_eofblocks_tag(
 
        pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
        spin_lock(&pag->pag_ici_lock);
-       trace_xfs_inode_set_eofblocks_tag(ip);
 
-       tagged = radix_tree_tagged(&pag->pag_ici_root,
-                                  XFS_ICI_EOFBLOCKS_TAG);
+       tagged = radix_tree_tagged(&pag->pag_ici_root, tag);
        radix_tree_tag_set(&pag->pag_ici_root,
-                          XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino),
-                          XFS_ICI_EOFBLOCKS_TAG);
+                          XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino), tag);
        if (!tagged) {
                /* propagate the eofblocks tag up into the perag radix tree */
                spin_lock(&ip->i_mount->m_perag_lock);
                radix_tree_tag_set(&ip->i_mount->m_perag_tree,
                                   XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino),
-                                  XFS_ICI_EOFBLOCKS_TAG);
+                                  tag);
                spin_unlock(&ip->i_mount->m_perag_lock);
 
                /* kick off background trimming */
-               xfs_queue_eofblocks(ip->i_mount);
+               execute(ip->i_mount);
 
-               trace_xfs_perag_set_eofblocks(ip->i_mount, pag->pag_agno,
-                                             -1, _RET_IP_);
+               set_tp(ip->i_mount, pag->pag_agno, -1, _RET_IP_);
        }
 
        spin_unlock(&pag->pag_ici_lock);
@@ -1453,8 +1507,21 @@ xfs_inode_set_eofblocks_tag(
 }
 
 void
-xfs_inode_clear_eofblocks_tag(
+xfs_inode_set_eofblocks_tag(
        xfs_inode_t     *ip)
+{
+       trace_xfs_inode_set_eofblocks_tag(ip);
+       return __xfs_inode_set_eofblocks_tag(ip, xfs_queue_eofblocks,
+                       trace_xfs_perag_set_eofblocks,
+                       XFS_ICI_EOFBLOCKS_TAG);
+}
+
+static void
+__xfs_inode_clear_eofblocks_tag(
+       xfs_inode_t     *ip,
+       void            (*clear_tp)(struct xfs_mount *mp, xfs_agnumber_t agno,
+                                   int error, unsigned long caller_ip),
+       int             tag)
 {
        struct xfs_mount *mp = ip->i_mount;
        struct xfs_perag *pag;
@@ -1465,23 +1532,141 @@ xfs_inode_clear_eofblocks_tag(
 
        pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
        spin_lock(&pag->pag_ici_lock);
-       trace_xfs_inode_clear_eofblocks_tag(ip);
 
        radix_tree_tag_clear(&pag->pag_ici_root,
-                            XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino),
-                            XFS_ICI_EOFBLOCKS_TAG);
-       if (!radix_tree_tagged(&pag->pag_ici_root, XFS_ICI_EOFBLOCKS_TAG)) {
+                            XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino), tag);
+       if (!radix_tree_tagged(&pag->pag_ici_root, tag)) {
                /* clear the eofblocks tag from the perag radix tree */
                spin_lock(&ip->i_mount->m_perag_lock);
                radix_tree_tag_clear(&ip->i_mount->m_perag_tree,
                                     XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino),
-                                    XFS_ICI_EOFBLOCKS_TAG);
+                                    tag);
                spin_unlock(&ip->i_mount->m_perag_lock);
-               trace_xfs_perag_clear_eofblocks(ip->i_mount, pag->pag_agno,
-                                              -1, _RET_IP_);
+               clear_tp(ip->i_mount, pag->pag_agno, -1, _RET_IP_);
        }
 
        spin_unlock(&pag->pag_ici_lock);
        xfs_perag_put(pag);
 }
 
+void
+xfs_inode_clear_eofblocks_tag(
+       xfs_inode_t     *ip)
+{
+       trace_xfs_inode_clear_eofblocks_tag(ip);
+       return __xfs_inode_clear_eofblocks_tag(ip,
+                       trace_xfs_perag_clear_eofblocks, XFS_ICI_EOFBLOCKS_TAG);
+}
+
+/*
+ * Automatic CoW Reservation Freeing
+ *
+ * These functions automatically garbage collect leftover CoW reservations
+ * that were made on behalf of a cowextsize hint when we start to run out
+ * of quota or when the reservations sit around for too long.  If the file
+ * has dirty pages or is undergoing writeback, its CoW reservations will
+ * be retained.
+ *
+ * The actual garbage collection piggybacks off the same code that runs
+ * the speculative EOF preallocation garbage collector.
+ */
+STATIC int
+xfs_inode_free_cowblocks(
+       struct xfs_inode        *ip,
+       int                     flags,
+       void                    *args)
+{
+       int ret;
+       struct xfs_eofblocks *eofb = args;
+       bool need_iolock = true;
+       int match;
+
+       ASSERT(!eofb || (eofb && eofb->eof_scan_owner != 0));
+
+       if (!xfs_reflink_has_real_cow_blocks(ip)) {
+               trace_xfs_inode_free_cowblocks_invalid(ip);
+               xfs_inode_clear_cowblocks_tag(ip);
+               return 0;
+       }
+
+       /*
+        * If the mapping is dirty or under writeback we cannot touch the
+        * CoW fork.  Leave it alone if we're in the midst of a directio.
+        */
+       if (mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_DIRTY) ||
+           mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_WRITEBACK) ||
+           atomic_read(&VFS_I(ip)->i_dio_count))
+               return 0;
+
+       if (eofb) {
+               if (eofb->eof_flags & XFS_EOF_FLAGS_UNION)
+                       match = xfs_inode_match_id_union(ip, eofb);
+               else
+                       match = xfs_inode_match_id(ip, eofb);
+               if (!match)
+                       return 0;
+
+               /* skip the inode if the file size is too small */
+               if (eofb->eof_flags & XFS_EOF_FLAGS_MINFILESIZE &&
+                   XFS_ISIZE(ip) < eofb->eof_min_file_size)
+                       return 0;
+
+               /*
+                * A scan owner implies we already hold the iolock. Skip it in
+                * xfs_free_eofblocks() to avoid deadlock. This also eliminates
+                * the possibility of EAGAIN being returned.
+                */
+               if (eofb->eof_scan_owner == ip->i_ino)
+                       need_iolock = false;
+       }
+
+       /* Free the CoW blocks */
+       if (need_iolock) {
+               xfs_ilock(ip, XFS_IOLOCK_EXCL);
+               xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
+       }
+
+       ret = xfs_reflink_cancel_cow_range(ip, 0, NULLFILEOFF);
+
+       if (need_iolock) {
+               xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);
+               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+       }
+
+       return ret;
+}
+
+int
+xfs_icache_free_cowblocks(
+       struct xfs_mount        *mp,
+       struct xfs_eofblocks    *eofb)
+{
+       return __xfs_icache_free_eofblocks(mp, eofb, xfs_inode_free_cowblocks,
+                       XFS_ICI_COWBLOCKS_TAG);
+}
+
+int
+xfs_inode_free_quota_cowblocks(
+       struct xfs_inode *ip)
+{
+       return __xfs_inode_free_quota_eofblocks(ip, xfs_icache_free_cowblocks);
+}
+
+void
+xfs_inode_set_cowblocks_tag(
+       xfs_inode_t     *ip)
+{
+       trace_xfs_inode_set_eofblocks_tag(ip);
+       return __xfs_inode_set_eofblocks_tag(ip, xfs_queue_cowblocks,
+                       trace_xfs_perag_set_eofblocks,
+                       XFS_ICI_COWBLOCKS_TAG);
+}
+
+void
+xfs_inode_clear_cowblocks_tag(
+       xfs_inode_t     *ip)
+{
+       trace_xfs_inode_clear_eofblocks_tag(ip);
+       return __xfs_inode_clear_eofblocks_tag(ip,
+                       trace_xfs_perag_clear_eofblocks, XFS_ICI_COWBLOCKS_TAG);
+}
index 05bac99bef75d9509ebbda00e81479bada9a225c..a1e02f4708abbfd6c942f4a5178709c58d681d8e 100644 (file)
@@ -40,6 +40,7 @@ struct xfs_eofblocks {
                                           in xfs_inode_ag_iterator */
 #define XFS_ICI_RECLAIM_TAG    0       /* inode is to be reclaimed */
 #define XFS_ICI_EOFBLOCKS_TAG  1       /* inode has blocks beyond EOF */
+#define XFS_ICI_COWBLOCKS_TAG  2       /* inode can have cow blocks to gc */
 
 /*
  * Flags for xfs_iget()
@@ -70,6 +71,12 @@ int xfs_inode_free_quota_eofblocks(struct xfs_inode *ip);
 void xfs_eofblocks_worker(struct work_struct *);
 void xfs_queue_eofblocks(struct xfs_mount *);
 
+void xfs_inode_set_cowblocks_tag(struct xfs_inode *ip);
+void xfs_inode_clear_cowblocks_tag(struct xfs_inode *ip);
+int xfs_icache_free_cowblocks(struct xfs_mount *, struct xfs_eofblocks *);
+int xfs_inode_free_quota_cowblocks(struct xfs_inode *ip);
+void xfs_cowblocks_worker(struct work_struct *);
+
 int xfs_inode_ag_iterator(struct xfs_mount *mp,
        int (*execute)(struct xfs_inode *ip, int flags, void *args),
        int flags, void *args);
index 624e1dfa716bac2ef3b5b7dc22bb30184f138ccd..4e560e6a12c1cc2d4b913292c62714bf78e9ae77 100644 (file)
@@ -49,6 +49,7 @@
 #include "xfs_trans_priv.h"
 #include "xfs_log.h"
 #include "xfs_bmap_btree.h"
+#include "xfs_reflink.h"
 
 kmem_zone_t *xfs_inode_zone;
 
@@ -76,6 +77,29 @@ xfs_get_extsz_hint(
        return 0;
 }
 
+/*
+ * Helper function to extract CoW extent size hint from inode.
+ * Between the extent size hint and the CoW extent size hint, we
+ * return the greater of the two.  If the value is zero (automatic),
+ * use the default size.
+ */
+xfs_extlen_t
+xfs_get_cowextsz_hint(
+       struct xfs_inode        *ip)
+{
+       xfs_extlen_t            a, b;
+
+       a = 0;
+       if (ip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE)
+               a = ip->i_d.di_cowextsize;
+       b = xfs_get_extsz_hint(ip);
+
+       a = max(a, b);
+       if (a == 0)
+               return XFS_DEFAULT_COWEXTSZ_HINT;
+       return a;
+}
+
 /*
  * These two are wrapper routines around the xfs_ilock() routine used to
  * centralize some grungy code.  They are used in places that wish to lock the
@@ -651,6 +675,8 @@ _xfs_dic2xflags(
        if (di_flags2 & XFS_DIFLAG2_ANY) {
                if (di_flags2 & XFS_DIFLAG2_DAX)
                        flags |= FS_XFLAG_DAX;
+               if (di_flags2 & XFS_DIFLAG2_COWEXTSIZE)
+                       flags |= FS_XFLAG_COWEXTSIZE;
        }
 
        if (has_attr)
@@ -834,6 +860,7 @@ xfs_ialloc(
        if (ip->i_d.di_version == 3) {
                inode->i_version = 1;
                ip->i_d.di_flags2 = 0;
+               ip->i_d.di_cowextsize = 0;
                ip->i_d.di_crtime.t_sec = (__int32_t)tv.tv_sec;
                ip->i_d.di_crtime.t_nsec = (__int32_t)tv.tv_nsec;
        }
@@ -896,6 +923,15 @@ xfs_ialloc(
                        ip->i_d.di_flags |= di_flags;
                        ip->i_d.di_flags2 |= di_flags2;
                }
+               if (pip &&
+                   (pip->i_d.di_flags2 & XFS_DIFLAG2_ANY) &&
+                   pip->i_d.di_version == 3 &&
+                   ip->i_d.di_version == 3) {
+                       if (pip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) {
+                               ip->i_d.di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
+                               ip->i_d.di_cowextsize = pip->i_d.di_cowextsize;
+                       }
+               }
                /* FALLTHROUGH */
        case S_IFLNK:
                ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
@@ -1586,6 +1622,20 @@ xfs_itruncate_extents(
                        goto out;
        }
 
+       /* Remove all pending CoW reservations. */
+       error = xfs_reflink_cancel_cow_blocks(ip, &tp, first_unmap_block,
+                       last_block);
+       if (error)
+               goto out;
+
+       /*
+        * Clear the reflink flag if we truncated everything.
+        */
+       if (ip->i_d.di_nblocks == 0 && xfs_is_reflink_inode(ip)) {
+               ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+               xfs_inode_clear_cowblocks_tag(ip);
+       }
+
        /*
         * Always re-log the inode so that our permanent transaction can keep
         * on rolling it forward in the log.
@@ -1850,6 +1900,7 @@ xfs_inactive(
        }
 
        mp = ip->i_mount;
+       ASSERT(!xfs_iflags_test(ip, XFS_IRECOVERY));
 
        /* If this is a read-only mount, don't do this (would generate I/O) */
        if (mp->m_flags & XFS_MOUNT_RDONLY)
index 8f30d2533b48995fd15d15bd23c1fd393e5c96f9..f14c1de2549db758f4bed9641fbfa99769570c71 100644 (file)
@@ -47,6 +47,7 @@ typedef struct xfs_inode {
 
        /* Extent information. */
        xfs_ifork_t             *i_afp;         /* attribute fork pointer */
+       xfs_ifork_t             *i_cowfp;       /* copy on write extents */
        xfs_ifork_t             i_df;           /* data fork */
 
        /* operations vectors */
@@ -65,6 +66,9 @@ typedef struct xfs_inode {
 
        struct xfs_icdinode     i_d;            /* most of ondisk inode */
 
+       xfs_extnum_t            i_cnextents;    /* # of extents in cow fork */
+       unsigned int            i_cformat;      /* format of cow fork */
+
        /* VFS inode */
        struct inode            i_vnode;        /* embedded VFS inode */
 } xfs_inode_t;
@@ -202,6 +206,11 @@ xfs_get_initial_prid(struct xfs_inode *dp)
        return XFS_PROJID_DEFAULT;
 }
 
+static inline bool xfs_is_reflink_inode(struct xfs_inode *ip)
+{
+       return ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK;
+}
+
 /*
  * In-core inode flags.
  */
@@ -217,6 +226,12 @@ xfs_get_initial_prid(struct xfs_inode *dp)
 #define XFS_IPINNED            (1 << __XFS_IPINNED_BIT)
 #define XFS_IDONTCACHE         (1 << 9) /* don't cache the inode long term */
 #define XFS_IEOFBLOCKS         (1 << 10)/* has the preallocblocks tag set */
+/*
+ * If this unlinked inode is in the middle of recovery, don't let drop_inode
+ * truncate and free the inode.  This can happen if we iget the inode during
+ * log recovery to replay a bmap operation on the inode.
+ */
+#define XFS_IRECOVERY          (1 << 11)
 
 /*
  * Per-lifetime flags need to be reset when re-using a reclaimable inode during
@@ -411,6 +426,7 @@ int         xfs_iflush(struct xfs_inode *, struct xfs_buf **);
 void           xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint);
 
 xfs_extlen_t   xfs_get_extsz_hint(struct xfs_inode *ip);
+xfs_extlen_t   xfs_get_cowextsz_hint(struct xfs_inode *ip);
 
 int            xfs_dir_ialloc(struct xfs_trans **, struct xfs_inode *, umode_t,
                               xfs_nlink_t, xfs_dev_t, prid_t, int,
@@ -474,4 +490,7 @@ do { \
 
 extern struct kmem_zone        *xfs_inode_zone;
 
+/* The default CoW extent size hint. */
+#define XFS_DEFAULT_COWEXTSZ_HINT 32
+
 #endif /* __XFS_INODE_H__ */
index 892c2aced20788cb9875dc3a9cddee78dc0b1b8b..9610e9c0095277c7b53feb9ad5e230d6b156327c 100644 (file)
@@ -368,7 +368,7 @@ xfs_inode_to_log_dinode(
                to->di_crtime.t_sec = from->di_crtime.t_sec;
                to->di_crtime.t_nsec = from->di_crtime.t_nsec;
                to->di_flags2 = from->di_flags2;
-
+               to->di_cowextsize = from->di_cowextsize;
                to->di_ino = ip->i_ino;
                to->di_lsn = lsn;
                memset(to->di_pad2, 0, sizeof(to->di_pad2));
index 0d9021f0551e4195cf01d259d7ecdd1b445e9310..c245bed3249bf1a192f430c7b7c781e84baf93e3 100644 (file)
@@ -903,6 +903,8 @@ xfs_ioc_fsgetxattr(
        xfs_ilock(ip, XFS_ILOCK_SHARED);
        fa.fsx_xflags = xfs_ip2xflags(ip);
        fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
+       fa.fsx_cowextsize = ip->i_d.di_cowextsize <<
+                       ip->i_mount->m_sb.sb_blocklog;
        fa.fsx_projid = xfs_get_projid(ip);
 
        if (attr) {
@@ -973,12 +975,13 @@ xfs_set_diflags(
        if (ip->i_d.di_version < 3)
                return;
 
-       di_flags2 = 0;
+       di_flags2 = (ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
        if (xflags & FS_XFLAG_DAX)
                di_flags2 |= XFS_DIFLAG2_DAX;
+       if (xflags & FS_XFLAG_COWEXTSIZE)
+               di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
 
        ip->i_d.di_flags2 = di_flags2;
-
 }
 
 STATIC void
@@ -1031,6 +1034,14 @@ xfs_ioctl_setattr_xflags(
                        return -EINVAL;
        }
 
+       /* Clear reflink if we are actually able to set the rt flag. */
+       if ((fa->fsx_xflags & FS_XFLAG_REALTIME) && xfs_is_reflink_inode(ip))
+               ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+
+       /* Don't allow us to set DAX mode for a reflinked file for now. */
+       if ((fa->fsx_xflags & FS_XFLAG_DAX) && xfs_is_reflink_inode(ip))
+               return -EINVAL;
+
        /*
         * Can't modify an immutable/append-only file unless
         * we have appropriate permission.
@@ -1219,6 +1230,56 @@ xfs_ioctl_setattr_check_extsize(
        return 0;
 }
 
+/*
+ * CoW extent size hint validation rules are:
+ *
+ * 1. CoW extent size hint can only be set if reflink is enabled on the fs.
+ *    The inode does not have to have any shared blocks, but it must be a v3.
+ * 2. FS_XFLAG_COWEXTSIZE is only valid for directories and regular files;
+ *    for a directory, the hint is propagated to new files.
+ * 3. Can be changed on files & directories at any time.
+ * 4. CoW extsize hint of 0 turns off hints, clears inode flags.
+ * 5. Extent size must be a multiple of the appropriate block size.
+ * 6. The extent size hint must be limited to half the AG size to avoid
+ *    alignment extending the extent beyond the limits of the AG.
+ */
+static int
+xfs_ioctl_setattr_check_cowextsize(
+       struct xfs_inode        *ip,
+       struct fsxattr          *fa)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+
+       if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE))
+               return 0;
+
+       if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb) ||
+           ip->i_d.di_version != 3)
+               return -EINVAL;
+
+       if (!S_ISREG(VFS_I(ip)->i_mode) && !S_ISDIR(VFS_I(ip)->i_mode))
+               return -EINVAL;
+
+       if (fa->fsx_cowextsize != 0) {
+               xfs_extlen_t    size;
+               xfs_fsblock_t   cowextsize_fsb;
+
+               cowextsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
+               if (cowextsize_fsb > MAXEXTLEN)
+                       return -EINVAL;
+
+               size = mp->m_sb.sb_blocksize;
+               if (cowextsize_fsb > mp->m_sb.sb_agblocks / 2)
+                       return -EINVAL;
+
+               if (fa->fsx_cowextsize % size)
+                       return -EINVAL;
+       } else
+               fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
+
+       return 0;
+}
+
 static int
 xfs_ioctl_setattr_check_projid(
        struct xfs_inode        *ip,
@@ -1311,6 +1372,10 @@ xfs_ioctl_setattr(
        if (code)
                goto error_trans_cancel;
 
+       code = xfs_ioctl_setattr_check_cowextsize(ip, fa);
+       if (code)
+               goto error_trans_cancel;
+
        code = xfs_ioctl_setattr_xflags(tp, ip, fa);
        if (code)
                goto error_trans_cancel;
@@ -1346,6 +1411,12 @@ xfs_ioctl_setattr(
                ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
        else
                ip->i_d.di_extsize = 0;
+       if (ip->i_d.di_version == 3 &&
+           (ip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
+               ip->i_d.di_cowextsize = fa->fsx_cowextsize >>
+                               mp->m_sb.sb_blocklog;
+       else
+               ip->i_d.di_cowextsize = 0;
 
        code = xfs_trans_commit(tp);
 
index c08253e11545d39eaa8426e371630bdf74b53e4f..d907eb9f8ef32a079f845c4aa4731bb3413fcb25 100644 (file)
@@ -39,6 +39,7 @@
 #include "xfs_quota.h"
 #include "xfs_dquot_item.h"
 #include "xfs_dquot.h"
+#include "xfs_reflink.h"
 
 
 #define XFS_WRITEIO_ALIGN(mp,off)      (((off) >> mp->m_writeio_log) \
@@ -70,7 +71,7 @@ xfs_bmbt_to_iomap(
        iomap->bdev = xfs_find_bdev_for_inode(VFS_I(ip));
 }
 
-static xfs_extlen_t
+xfs_extlen_t
 xfs_eof_alignment(
        struct xfs_inode        *ip,
        xfs_extlen_t            extsize)
@@ -609,7 +610,7 @@ xfs_file_iomap_begin_delay(
        }
 
 retry:
-       error = xfs_bmapi_reserve_delalloc(ip, offset_fsb,
+       error = xfs_bmapi_reserve_delalloc(ip, XFS_DATA_FORK, offset_fsb,
                        end_fsb - offset_fsb, &got,
                        &prev, &idx, eof);
        switch (error) {
@@ -666,6 +667,7 @@ out_unlock:
 int
 xfs_iomap_write_allocate(
        xfs_inode_t     *ip,
+       int             whichfork,
        xfs_off_t       offset,
        xfs_bmbt_irec_t *imap)
 {
@@ -678,8 +680,12 @@ xfs_iomap_write_allocate(
        xfs_trans_t     *tp;
        int             nimaps;
        int             error = 0;
+       int             flags = 0;
        int             nres;
 
+       if (whichfork == XFS_COW_FORK)
+               flags |= XFS_BMAPI_COWFORK;
+
        /*
         * Make sure that the dquots are there.
         */
@@ -773,7 +779,7 @@ xfs_iomap_write_allocate(
                         * pointer that the caller gave to us.
                         */
                        error = xfs_bmapi_write(tp, ip, map_start_fsb,
-                                               count_fsb, 0, &first_block,
+                                               count_fsb, flags, &first_block,
                                                nres, imap, &nimaps,
                                                &dfops);
                        if (error)
@@ -955,14 +961,22 @@ xfs_file_iomap_begin(
        struct xfs_mount        *mp = ip->i_mount;
        struct xfs_bmbt_irec    imap;
        xfs_fileoff_t           offset_fsb, end_fsb;
+       bool                    shared, trimmed;
        int                     nimaps = 1, error = 0;
        unsigned                lockmode;
 
        if (XFS_FORCED_SHUTDOWN(mp))
                return -EIO;
 
-       if ((flags & IOMAP_WRITE) &&
-           !IS_DAX(inode) && !xfs_get_extsz_hint(ip)) {
+       if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && xfs_is_reflink_inode(ip)) {
+               error = xfs_reflink_reserve_cow_range(ip, offset, length);
+               if (error < 0)
+                       return error;
+       }
+
+       if ((flags & IOMAP_WRITE) && !IS_DAX(inode) &&
+                  !xfs_get_extsz_hint(ip)) {
+               /* Reserve delalloc blocks for regular writeback. */
                return xfs_file_iomap_begin_delay(inode, offset, length, flags,
                                iomap);
        }
@@ -976,7 +990,14 @@ xfs_file_iomap_begin(
        end_fsb = XFS_B_TO_FSB(mp, offset + length);
 
        error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
-                              &nimaps, XFS_BMAPI_ENTIRE);
+                              &nimaps, 0);
+       if (error) {
+               xfs_iunlock(ip, lockmode);
+               return error;
+       }
+
+       /* Trim the mapping to the nearest shared extent boundary. */
+       error = xfs_reflink_trim_around_shared(ip, &imap, &shared, &trimmed);
        if (error) {
                xfs_iunlock(ip, lockmode);
                return error;
@@ -1015,6 +1036,8 @@ xfs_file_iomap_begin(
        }
 
        xfs_bmbt_to_iomap(ip, iomap, &imap);
+       if (shared)
+               iomap->flags |= IOMAP_F_SHARED;
        return 0;
 }
 
index 6498be485932816f29c3ab3ef007880e36a5350d..6d45cf01fcffb01e123cb75d3a2707318c4cac31 100644 (file)
@@ -25,12 +25,13 @@ struct xfs_bmbt_irec;
 
 int xfs_iomap_write_direct(struct xfs_inode *, xfs_off_t, size_t,
                        struct xfs_bmbt_irec *, int);
-int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t,
+int xfs_iomap_write_allocate(struct xfs_inode *, int, xfs_off_t,
                        struct xfs_bmbt_irec *);
 int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t);
 
 void xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *,
                struct xfs_bmbt_irec *);
+xfs_extlen_t xfs_eof_alignment(struct xfs_inode *ip, xfs_extlen_t extsize);
 
 extern struct iomap_ops xfs_iomap_ops;
 extern struct iomap_ops xfs_xattr_iomap_ops;
index c5da95eb79b8742543a3559c797c6e967fc1c9ed..405a65cd9d6bcf4c7fdf21f502ee267c0af7e970 100644 (file)
@@ -1159,6 +1159,7 @@ xfs_diflags_to_iflags(
                inode->i_flags |= S_NOATIME;
        if (S_ISREG(inode->i_mode) &&
            ip->i_mount->m_sb.sb_blocksize == PAGE_SIZE &&
+           !xfs_is_reflink_inode(ip) &&
            (ip->i_mount->m_flags & XFS_MOUNT_DAX ||
             ip->i_d.di_flags2 & XFS_DIFLAG2_DAX))
                inode->i_flags |= S_DAX;
index ce73eb34620dbbf06570650a582163a98a0d8f92..66e881790c17109496e21bd5e2d7d21f5ecc7fe5 100644 (file)
@@ -66,7 +66,7 @@ xfs_bulkstat_one_int(
        if (!buffer || xfs_internal_inum(mp, ino))
                return -EINVAL;
 
-       buf = kmem_alloc(sizeof(*buf), KM_SLEEP | KM_MAYFAIL);
+       buf = kmem_zalloc(sizeof(*buf), KM_SLEEP | KM_MAYFAIL);
        if (!buf)
                return -ENOMEM;
 
@@ -111,6 +111,12 @@ xfs_bulkstat_one_int(
        buf->bs_aextents = dic->di_anextents;
        buf->bs_forkoff = XFS_IFORK_BOFF(ip);
 
+       if (dic->di_version == 3) {
+               if (dic->di_flags2 & XFS_DIFLAG2_COWEXTSIZE)
+                       buf->bs_cowextsize = dic->di_cowextsize <<
+                                       mp->m_sb.sb_blocklog;
+       }
+
        switch (dic->di_format) {
        case XFS_DINODE_FMT_DEV:
                buf->bs_rdev = ip->i_df.if_u2.if_rdev;
index b8d64d520e125ca1e1ad78742450f51ea04fe013..68640fb63a542346cac31553dd0aad1a78421a16 100644 (file)
@@ -116,6 +116,7 @@ typedef __u32                       xfs_nlink_t;
 #define xfs_inherit_nodefrag   xfs_params.inherit_nodfrg.val
 #define xfs_fstrm_centisecs    xfs_params.fstrm_timer.val
 #define xfs_eofb_secs          xfs_params.eofb_timer.val
+#define xfs_cowb_secs          xfs_params.cowb_timer.val
 
 #define current_cpu()          (raw_smp_processor_id())
 #define current_pid()          (current->pid)
index 846483d5694922f53b8d08c440b170dd3c88ca72..9b3d7c76915d9c92948bed063d79bd10a6107a77 100644 (file)
@@ -45,6 +45,8 @@
 #include "xfs_dir2.h"
 #include "xfs_rmap_item.h"
 #include "xfs_buf_item.h"
+#include "xfs_refcount_item.h"
+#include "xfs_bmap_item.h"
 
 #define BLK_AVG(blk1, blk2)    ((blk1+blk2) >> 1)
 
@@ -1924,6 +1926,10 @@ xlog_recover_reorder_trans(
                case XFS_LI_EFI:
                case XFS_LI_RUI:
                case XFS_LI_RUD:
+               case XFS_LI_CUI:
+               case XFS_LI_CUD:
+               case XFS_LI_BUI:
+               case XFS_LI_BUD:
                        trace_xfs_log_recover_item_reorder_tail(log,
                                                        trans, item, pass);
                        list_move_tail(&item->ri_list, &inode_list);
@@ -2242,6 +2248,7 @@ xlog_recover_get_buf_lsn(
        case XFS_ABTB_MAGIC:
        case XFS_ABTC_MAGIC:
        case XFS_RMAP_CRC_MAGIC:
+       case XFS_REFC_CRC_MAGIC:
        case XFS_IBT_CRC_MAGIC:
        case XFS_IBT_MAGIC: {
                struct xfs_btree_block *btb = blk;
@@ -2415,6 +2422,9 @@ xlog_recover_validate_buf_type(
                case XFS_RMAP_CRC_MAGIC:
                        bp->b_ops = &xfs_rmapbt_buf_ops;
                        break;
+               case XFS_REFC_CRC_MAGIC:
+                       bp->b_ops = &xfs_refcountbt_buf_ops;
+                       break;
                default:
                        warnmsg = "Bad btree block magic!";
                        break;
@@ -3546,6 +3556,242 @@ xlog_recover_rud_pass2(
        return 0;
 }
 
+/*
+ * Copy an CUI format buffer from the given buf, and into the destination
+ * CUI format structure.  The CUI/CUD items were designed not to need any
+ * special alignment handling.
+ */
+static int
+xfs_cui_copy_format(
+       struct xfs_log_iovec            *buf,
+       struct xfs_cui_log_format       *dst_cui_fmt)
+{
+       struct xfs_cui_log_format       *src_cui_fmt;
+       uint                            len;
+
+       src_cui_fmt = buf->i_addr;
+       len = xfs_cui_log_format_sizeof(src_cui_fmt->cui_nextents);
+
+       if (buf->i_len == len) {
+               memcpy(dst_cui_fmt, src_cui_fmt, len);
+               return 0;
+       }
+       return -EFSCORRUPTED;
+}
+
+/*
+ * This routine is called to create an in-core extent refcount update
+ * item from the cui format structure which was logged on disk.
+ * It allocates an in-core cui, copies the extents from the format
+ * structure into it, and adds the cui to the AIL with the given
+ * LSN.
+ */
+STATIC int
+xlog_recover_cui_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item,
+       xfs_lsn_t                       lsn)
+{
+       int                             error;
+       struct xfs_mount                *mp = log->l_mp;
+       struct xfs_cui_log_item         *cuip;
+       struct xfs_cui_log_format       *cui_formatp;
+
+       cui_formatp = item->ri_buf[0].i_addr;
+
+       cuip = xfs_cui_init(mp, cui_formatp->cui_nextents);
+       error = xfs_cui_copy_format(&item->ri_buf[0], &cuip->cui_format);
+       if (error) {
+               xfs_cui_item_free(cuip);
+               return error;
+       }
+       atomic_set(&cuip->cui_next_extent, cui_formatp->cui_nextents);
+
+       spin_lock(&log->l_ailp->xa_lock);
+       /*
+        * The CUI has two references. One for the CUD and one for CUI to ensure
+        * it makes it into the AIL. Insert the CUI into the AIL directly and
+        * drop the CUI reference. Note that xfs_trans_ail_update() drops the
+        * AIL lock.
+        */
+       xfs_trans_ail_update(log->l_ailp, &cuip->cui_item, lsn);
+       xfs_cui_release(cuip);
+       return 0;
+}
+
+
+/*
+ * This routine is called when an CUD format structure is found in a committed
+ * transaction in the log. Its purpose is to cancel the corresponding CUI if it
+ * was still in the log. To do this it searches the AIL for the CUI with an id
+ * equal to that in the CUD format structure. If we find it we drop the CUD
+ * reference, which removes the CUI from the AIL and frees it.
+ */
+STATIC int
+xlog_recover_cud_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item)
+{
+       struct xfs_cud_log_format       *cud_formatp;
+       struct xfs_cui_log_item         *cuip = NULL;
+       struct xfs_log_item             *lip;
+       __uint64_t                      cui_id;
+       struct xfs_ail_cursor           cur;
+       struct xfs_ail                  *ailp = log->l_ailp;
+
+       cud_formatp = item->ri_buf[0].i_addr;
+       if (item->ri_buf[0].i_len != sizeof(struct xfs_cud_log_format))
+               return -EFSCORRUPTED;
+       cui_id = cud_formatp->cud_cui_id;
+
+       /*
+        * Search for the CUI with the id in the CUD format structure in the
+        * AIL.
+        */
+       spin_lock(&ailp->xa_lock);
+       lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+       while (lip != NULL) {
+               if (lip->li_type == XFS_LI_CUI) {
+                       cuip = (struct xfs_cui_log_item *)lip;
+                       if (cuip->cui_format.cui_id == cui_id) {
+                               /*
+                                * Drop the CUD reference to the CUI. This
+                                * removes the CUI from the AIL and frees it.
+                                */
+                               spin_unlock(&ailp->xa_lock);
+                               xfs_cui_release(cuip);
+                               spin_lock(&ailp->xa_lock);
+                               break;
+                       }
+               }
+               lip = xfs_trans_ail_cursor_next(ailp, &cur);
+       }
+
+       xfs_trans_ail_cursor_done(&cur);
+       spin_unlock(&ailp->xa_lock);
+
+       return 0;
+}
+
+/*
+ * Copy an BUI format buffer from the given buf, and into the destination
+ * BUI format structure.  The BUI/BUD items were designed not to need any
+ * special alignment handling.
+ */
+static int
+xfs_bui_copy_format(
+       struct xfs_log_iovec            *buf,
+       struct xfs_bui_log_format       *dst_bui_fmt)
+{
+       struct xfs_bui_log_format       *src_bui_fmt;
+       uint                            len;
+
+       src_bui_fmt = buf->i_addr;
+       len = xfs_bui_log_format_sizeof(src_bui_fmt->bui_nextents);
+
+       if (buf->i_len == len) {
+               memcpy(dst_bui_fmt, src_bui_fmt, len);
+               return 0;
+       }
+       return -EFSCORRUPTED;
+}
+
+/*
+ * This routine is called to create an in-core extent bmap update
+ * item from the bui format structure which was logged on disk.
+ * It allocates an in-core bui, copies the extents from the format
+ * structure into it, and adds the bui to the AIL with the given
+ * LSN.
+ */
+STATIC int
+xlog_recover_bui_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item,
+       xfs_lsn_t                       lsn)
+{
+       int                             error;
+       struct xfs_mount                *mp = log->l_mp;
+       struct xfs_bui_log_item         *buip;
+       struct xfs_bui_log_format       *bui_formatp;
+
+       bui_formatp = item->ri_buf[0].i_addr;
+
+       if (bui_formatp->bui_nextents != XFS_BUI_MAX_FAST_EXTENTS)
+               return -EFSCORRUPTED;
+       buip = xfs_bui_init(mp);
+       error = xfs_bui_copy_format(&item->ri_buf[0], &buip->bui_format);
+       if (error) {
+               xfs_bui_item_free(buip);
+               return error;
+       }
+       atomic_set(&buip->bui_next_extent, bui_formatp->bui_nextents);
+
+       spin_lock(&log->l_ailp->xa_lock);
+       /*
+        * The RUI has two references. One for the RUD and one for RUI to ensure
+        * it makes it into the AIL. Insert the RUI into the AIL directly and
+        * drop the RUI reference. Note that xfs_trans_ail_update() drops the
+        * AIL lock.
+        */
+       xfs_trans_ail_update(log->l_ailp, &buip->bui_item, lsn);
+       xfs_bui_release(buip);
+       return 0;
+}
+
+
+/*
+ * This routine is called when an BUD format structure is found in a committed
+ * transaction in the log. Its purpose is to cancel the corresponding BUI if it
+ * was still in the log. To do this it searches the AIL for the BUI with an id
+ * equal to that in the BUD format structure. If we find it we drop the BUD
+ * reference, which removes the BUI from the AIL and frees it.
+ */
+STATIC int
+xlog_recover_bud_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item)
+{
+       struct xfs_bud_log_format       *bud_formatp;
+       struct xfs_bui_log_item         *buip = NULL;
+       struct xfs_log_item             *lip;
+       __uint64_t                      bui_id;
+       struct xfs_ail_cursor           cur;
+       struct xfs_ail                  *ailp = log->l_ailp;
+
+       bud_formatp = item->ri_buf[0].i_addr;
+       if (item->ri_buf[0].i_len != sizeof(struct xfs_bud_log_format))
+               return -EFSCORRUPTED;
+       bui_id = bud_formatp->bud_bui_id;
+
+       /*
+        * Search for the BUI with the id in the BUD format structure in the
+        * AIL.
+        */
+       spin_lock(&ailp->xa_lock);
+       lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+       while (lip != NULL) {
+               if (lip->li_type == XFS_LI_BUI) {
+                       buip = (struct xfs_bui_log_item *)lip;
+                       if (buip->bui_format.bui_id == bui_id) {
+                               /*
+                                * Drop the BUD reference to the BUI. This
+                                * removes the BUI from the AIL and frees it.
+                                */
+                               spin_unlock(&ailp->xa_lock);
+                               xfs_bui_release(buip);
+                               spin_lock(&ailp->xa_lock);
+                               break;
+                       }
+               }
+               lip = xfs_trans_ail_cursor_next(ailp, &cur);
+       }
+
+       xfs_trans_ail_cursor_done(&cur);
+       spin_unlock(&ailp->xa_lock);
+
+       return 0;
+}
+
 /*
  * This routine is called when an inode create format structure is found in a
  * committed transaction in the log.  It's purpose is to initialise the inodes
@@ -3773,6 +4019,10 @@ xlog_recover_ra_pass2(
        case XFS_LI_QUOTAOFF:
        case XFS_LI_RUI:
        case XFS_LI_RUD:
+       case XFS_LI_CUI:
+       case XFS_LI_CUD:
+       case XFS_LI_BUI:
+       case XFS_LI_BUD:
        default:
                break;
        }
@@ -3798,6 +4048,10 @@ xlog_recover_commit_pass1(
        case XFS_LI_ICREATE:
        case XFS_LI_RUI:
        case XFS_LI_RUD:
+       case XFS_LI_CUI:
+       case XFS_LI_CUD:
+       case XFS_LI_BUI:
+       case XFS_LI_BUD:
                /* nothing to do in pass 1 */
                return 0;
        default:
@@ -3832,6 +4086,14 @@ xlog_recover_commit_pass2(
                return xlog_recover_rui_pass2(log, item, trans->r_lsn);
        case XFS_LI_RUD:
                return xlog_recover_rud_pass2(log, item);
+       case XFS_LI_CUI:
+               return xlog_recover_cui_pass2(log, item, trans->r_lsn);
+       case XFS_LI_CUD:
+               return xlog_recover_cud_pass2(log, item);
+       case XFS_LI_BUI:
+               return xlog_recover_bui_pass2(log, item, trans->r_lsn);
+       case XFS_LI_BUD:
+               return xlog_recover_bud_pass2(log, item);
        case XFS_LI_DQUOT:
                return xlog_recover_dquot_pass2(log, buffer_list, item,
                                                trans->r_lsn);
@@ -4419,12 +4681,94 @@ xlog_recover_cancel_rui(
        spin_lock(&ailp->xa_lock);
 }
 
+/* Recover the CUI if necessary. */
+STATIC int
+xlog_recover_process_cui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_cui_log_item         *cuip;
+       int                             error;
+
+       /*
+        * Skip CUIs that we've already processed.
+        */
+       cuip = container_of(lip, struct xfs_cui_log_item, cui_item);
+       if (test_bit(XFS_CUI_RECOVERED, &cuip->cui_flags))
+               return 0;
+
+       spin_unlock(&ailp->xa_lock);
+       error = xfs_cui_recover(mp, cuip);
+       spin_lock(&ailp->xa_lock);
+
+       return error;
+}
+
+/* Release the CUI since we're cancelling everything. */
+STATIC void
+xlog_recover_cancel_cui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_cui_log_item         *cuip;
+
+       cuip = container_of(lip, struct xfs_cui_log_item, cui_item);
+
+       spin_unlock(&ailp->xa_lock);
+       xfs_cui_release(cuip);
+       spin_lock(&ailp->xa_lock);
+}
+
+/* Recover the BUI if necessary. */
+STATIC int
+xlog_recover_process_bui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_bui_log_item         *buip;
+       int                             error;
+
+       /*
+        * Skip BUIs that we've already processed.
+        */
+       buip = container_of(lip, struct xfs_bui_log_item, bui_item);
+       if (test_bit(XFS_BUI_RECOVERED, &buip->bui_flags))
+               return 0;
+
+       spin_unlock(&ailp->xa_lock);
+       error = xfs_bui_recover(mp, buip);
+       spin_lock(&ailp->xa_lock);
+
+       return error;
+}
+
+/* Release the BUI since we're cancelling everything. */
+STATIC void
+xlog_recover_cancel_bui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_bui_log_item         *buip;
+
+       buip = container_of(lip, struct xfs_bui_log_item, bui_item);
+
+       spin_unlock(&ailp->xa_lock);
+       xfs_bui_release(buip);
+       spin_lock(&ailp->xa_lock);
+}
+
 /* Is this log item a deferred action intent? */
 static inline bool xlog_item_is_intent(struct xfs_log_item *lip)
 {
        switch (lip->li_type) {
        case XFS_LI_EFI:
        case XFS_LI_RUI:
+       case XFS_LI_CUI:
+       case XFS_LI_BUI:
                return true;
        default:
                return false;
@@ -4488,6 +4832,12 @@ xlog_recover_process_intents(
                case XFS_LI_RUI:
                        error = xlog_recover_process_rui(log->l_mp, ailp, lip);
                        break;
+               case XFS_LI_CUI:
+                       error = xlog_recover_process_cui(log->l_mp, ailp, lip);
+                       break;
+               case XFS_LI_BUI:
+                       error = xlog_recover_process_bui(log->l_mp, ailp, lip);
+                       break;
                }
                if (error)
                        goto out;
@@ -4535,6 +4885,12 @@ xlog_recover_cancel_intents(
                case XFS_LI_RUI:
                        xlog_recover_cancel_rui(log->l_mp, ailp, lip);
                        break;
+               case XFS_LI_CUI:
+                       xlog_recover_cancel_cui(log->l_mp, ailp, lip);
+                       break;
+               case XFS_LI_BUI:
+                       xlog_recover_cancel_bui(log->l_mp, ailp, lip);
+                       break;
                }
 
                lip = xfs_trans_ail_cursor_next(ailp, &cur);
@@ -4613,6 +4969,7 @@ xlog_recover_process_one_iunlink(
        if (error)
                goto fail_iput;
 
+       xfs_iflags_clear(ip, XFS_IRECOVERY);
        ASSERT(VFS_I(ip)->i_nlink == 0);
        ASSERT(VFS_I(ip)->i_mode != 0);
 
index 56e85a6c85c7681689d5b985f367bb7c3ac95b5f..fc7873942bea51866611aee5a7437f3d4a036a67 100644 (file)
@@ -43,6 +43,8 @@
 #include "xfs_icache.h"
 #include "xfs_sysfs.h"
 #include "xfs_rmap_btree.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_reflink.h"
 
 
 static DEFINE_MUTEX(xfs_uuid_table_mutex);
@@ -684,6 +686,7 @@ xfs_mountfs(
        xfs_bmap_compute_maxlevels(mp, XFS_ATTR_FORK);
        xfs_ialloc_compute_maxlevels(mp);
        xfs_rmapbt_compute_maxlevels(mp);
+       xfs_refcountbt_compute_maxlevels(mp);
 
        xfs_set_maxicount(mp);
 
@@ -922,6 +925,15 @@ xfs_mountfs(
                }
        }
 
+       /*
+        * During the second phase of log recovery, we need iget and
+        * iput to behave like they do for an active filesystem.
+        * xfs_fs_drop_inode needs to be able to prevent the deletion
+        * of inodes before we're done replaying log items on those
+        * inodes.
+        */
+       mp->m_super->s_flags |= MS_ACTIVE;
+
        /*
         * Finish recovering the file system.  This part needed to be delayed
         * until after the root and real-time bitmap inodes were consistently
@@ -974,10 +986,28 @@ xfs_mountfs(
                if (error)
                        xfs_warn(mp,
        "Unable to allocate reserve blocks. Continuing without reserve pool.");
+
+               /* Recover any CoW blocks that never got remapped. */
+               error = xfs_reflink_recover_cow(mp);
+               if (error) {
+                       xfs_err(mp,
+       "Error %d recovering leftover CoW allocations.", error);
+                       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+                       goto out_quota;
+               }
+
+               /* Reserve AG blocks for future btree expansion. */
+               error = xfs_fs_reserve_ag_blocks(mp);
+               if (error && error != -ENOSPC)
+                       goto out_agresv;
        }
 
        return 0;
 
+ out_agresv:
+       xfs_fs_unreserve_ag_blocks(mp);
+ out_quota:
+       xfs_qm_unmount_quotas(mp);
  out_rtunmount:
        xfs_rtunmount_inodes(mp);
  out_rele_rip:
@@ -1019,7 +1049,9 @@ xfs_unmountfs(
        int                     error;
 
        cancel_delayed_work_sync(&mp->m_eofblocks_work);
+       cancel_delayed_work_sync(&mp->m_cowblocks_work);
 
+       xfs_fs_unreserve_ag_blocks(mp);
        xfs_qm_unmount_quotas(mp);
        xfs_rtunmount_inodes(mp);
        IRELE(mp->m_rootip);
index 041d9493e7988548dc2fb9e2e1585eada20b27f2..819b80b15bfb11146962539a366ecf2efa093f96 100644 (file)
@@ -124,10 +124,13 @@ typedef struct xfs_mount {
        uint                    m_inobt_mnr[2]; /* min inobt btree records */
        uint                    m_rmap_mxr[2];  /* max rmap btree records */
        uint                    m_rmap_mnr[2];  /* min rmap btree records */
+       uint                    m_refc_mxr[2];  /* max refc btree records */
+       uint                    m_refc_mnr[2];  /* min refc btree records */
        uint                    m_ag_maxlevels; /* XFS_AG_MAXLEVELS */
        uint                    m_bm_maxlevels[2]; /* XFS_BM_MAXLEVELS */
        uint                    m_in_maxlevels; /* max inobt btree levels. */
        uint                    m_rmap_maxlevels; /* max rmap btree levels */
+       uint                    m_refc_maxlevels; /* max refcount btree level */
        xfs_extlen_t            m_ag_prealloc_blocks; /* reserved ag blocks */
        uint                    m_alloc_set_aside; /* space we can't use */
        uint                    m_ag_max_usable; /* max space per AG */
@@ -161,6 +164,8 @@ typedef struct xfs_mount {
        struct delayed_work     m_reclaim_work; /* background inode reclaim */
        struct delayed_work     m_eofblocks_work; /* background eof blocks
                                                     trimming */
+       struct delayed_work     m_cowblocks_work; /* background cow blocks
+                                                    trimming */
        bool                    m_update_sb;    /* sb needs update in mount */
        int64_t                 m_low_space[XFS_LOWSP_MAX];
                                                /* low free space thresholds */
@@ -399,6 +404,9 @@ typedef struct xfs_perag {
        struct xfs_ag_resv      pag_meta_resv;
        /* Blocks reserved for just AGFL-based metadata. */
        struct xfs_ag_resv      pag_agfl_resv;
+
+       /* reference count */
+       __uint8_t               pagf_refcount_level;
 } xfs_perag_t;
 
 static inline struct xfs_ag_resv *
index 69e2986a377619876ac3757ed6d255db9433b91a..0c381d71b242ec8553be6e01b8400977c4403365 100644 (file)
@@ -49,6 +49,8 @@ xfs_check_ondisk_structs(void)
        XFS_CHECK_STRUCT_SIZE(struct xfs_dsymlink_hdr,          56);
        XFS_CHECK_STRUCT_SIZE(struct xfs_inobt_key,             4);
        XFS_CHECK_STRUCT_SIZE(struct xfs_inobt_rec,             16);
+       XFS_CHECK_STRUCT_SIZE(struct xfs_refcount_key,          4);
+       XFS_CHECK_STRUCT_SIZE(struct xfs_refcount_rec,          12);
        XFS_CHECK_STRUCT_SIZE(struct xfs_rmap_key,              20);
        XFS_CHECK_STRUCT_SIZE(struct xfs_rmap_rec,              24);
        XFS_CHECK_STRUCT_SIZE(struct xfs_timestamp,             8);
@@ -56,6 +58,7 @@ xfs_check_ondisk_structs(void)
        XFS_CHECK_STRUCT_SIZE(xfs_alloc_ptr_t,                  4);
        XFS_CHECK_STRUCT_SIZE(xfs_alloc_rec_t,                  8);
        XFS_CHECK_STRUCT_SIZE(xfs_inobt_ptr_t,                  4);
+       XFS_CHECK_STRUCT_SIZE(xfs_refcount_ptr_t,               4);
        XFS_CHECK_STRUCT_SIZE(xfs_rmap_ptr_t,                   4);
 
        /* dir/attr trees */
index 0f14b2e4bf6cb03b803ff42abbf1c026fc74c70d..93a7aafa56d6fdd9b76246f4e5c3196fdc17f985 100644 (file)
@@ -113,6 +113,13 @@ xfs_fs_map_blocks(
        if (XFS_IS_REALTIME_INODE(ip))
                return -ENXIO;
 
+       /*
+        * The pNFS block layout spec actually supports reflink like
+        * functionality, but the Linux pNFS server doesn't implement it yet.
+        */
+       if (xfs_is_reflink_inode(ip))
+               return -ENXIO;
+
        /*
         * Lock out any other I/O before we flush and invalidate the pagecache,
         * and then hand out a layout to the remote system.  This is very
diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c
new file mode 100644 (file)
index 0000000..fe86a66
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_bit.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_buf_item.h"
+#include "xfs_refcount_item.h"
+#include "xfs_log.h"
+#include "xfs_refcount.h"
+
+
+kmem_zone_t    *xfs_cui_zone;
+kmem_zone_t    *xfs_cud_zone;
+
+static inline struct xfs_cui_log_item *CUI_ITEM(struct xfs_log_item *lip)
+{
+       return container_of(lip, struct xfs_cui_log_item, cui_item);
+}
+
+void
+xfs_cui_item_free(
+       struct xfs_cui_log_item *cuip)
+{
+       if (cuip->cui_format.cui_nextents > XFS_CUI_MAX_FAST_EXTENTS)
+               kmem_free(cuip);
+       else
+               kmem_zone_free(xfs_cui_zone, cuip);
+}
+
+STATIC void
+xfs_cui_item_size(
+       struct xfs_log_item     *lip,
+       int                     *nvecs,
+       int                     *nbytes)
+{
+       struct xfs_cui_log_item *cuip = CUI_ITEM(lip);
+
+       *nvecs += 1;
+       *nbytes += xfs_cui_log_format_sizeof(cuip->cui_format.cui_nextents);
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given cui log item. We use only 1 iovec, and we point that
+ * at the cui_log_format structure embedded in the cui item.
+ * It is at this point that we assert that all of the extent
+ * slots in the cui item have been filled.
+ */
+STATIC void
+xfs_cui_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_vec      *lv)
+{
+       struct xfs_cui_log_item *cuip = CUI_ITEM(lip);
+       struct xfs_log_iovec    *vecp = NULL;
+
+       ASSERT(atomic_read(&cuip->cui_next_extent) ==
+                       cuip->cui_format.cui_nextents);
+
+       cuip->cui_format.cui_type = XFS_LI_CUI;
+       cuip->cui_format.cui_size = 1;
+
+       xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_CUI_FORMAT, &cuip->cui_format,
+                       xfs_cui_log_format_sizeof(cuip->cui_format.cui_nextents));
+}
+
+/*
+ * Pinning has no meaning for an cui item, so just return.
+ */
+STATIC void
+xfs_cui_item_pin(
+       struct xfs_log_item     *lip)
+{
+}
+
+/*
+ * The unpin operation is the last place an CUI is manipulated in the log. It is
+ * either inserted in the AIL or aborted in the event of a log I/O error. In
+ * either case, the CUI transaction has been successfully committed to make it
+ * this far. Therefore, we expect whoever committed the CUI to either construct
+ * and commit the CUD or drop the CUD's reference in the event of error. Simply
+ * drop the log's CUI reference now that the log is done with it.
+ */
+STATIC void
+xfs_cui_item_unpin(
+       struct xfs_log_item     *lip,
+       int                     remove)
+{
+       struct xfs_cui_log_item *cuip = CUI_ITEM(lip);
+
+       xfs_cui_release(cuip);
+}
+
+/*
+ * CUI items have no locking or pushing.  However, since CUIs are pulled from
+ * the AIL when their corresponding CUDs are committed to disk, their situation
+ * is very similar to being pinned.  Return XFS_ITEM_PINNED so that the caller
+ * will eventually flush the log.  This should help in getting the CUI out of
+ * the AIL.
+ */
+STATIC uint
+xfs_cui_item_push(
+       struct xfs_log_item     *lip,
+       struct list_head        *buffer_list)
+{
+       return XFS_ITEM_PINNED;
+}
+
+/*
+ * The CUI has been either committed or aborted if the transaction has been
+ * cancelled. If the transaction was cancelled, an CUD isn't going to be
+ * constructed and thus we free the CUI here directly.
+ */
+STATIC void
+xfs_cui_item_unlock(
+       struct xfs_log_item     *lip)
+{
+       if (lip->li_flags & XFS_LI_ABORTED)
+               xfs_cui_item_free(CUI_ITEM(lip));
+}
+
+/*
+ * The CUI is logged only once and cannot be moved in the log, so simply return
+ * the lsn at which it's been logged.
+ */
+STATIC xfs_lsn_t
+xfs_cui_item_committed(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+       return lsn;
+}
+
+/*
+ * The CUI dependency tracking op doesn't do squat.  It can't because
+ * it doesn't know where the free extent is coming from.  The dependency
+ * tracking has to be handled by the "enclosing" metadata object.  For
+ * example, for inodes, the inode is locked throughout the extent freeing
+ * so the dependency should be recorded there.
+ */
+STATIC void
+xfs_cui_item_committing(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+}
+
+/*
+ * This is the ops vector shared by all cui log items.
+ */
+static const struct xfs_item_ops xfs_cui_item_ops = {
+       .iop_size       = xfs_cui_item_size,
+       .iop_format     = xfs_cui_item_format,
+       .iop_pin        = xfs_cui_item_pin,
+       .iop_unpin      = xfs_cui_item_unpin,
+       .iop_unlock     = xfs_cui_item_unlock,
+       .iop_committed  = xfs_cui_item_committed,
+       .iop_push       = xfs_cui_item_push,
+       .iop_committing = xfs_cui_item_committing,
+};
+
+/*
+ * Allocate and initialize an cui item with the given number of extents.
+ */
+struct xfs_cui_log_item *
+xfs_cui_init(
+       struct xfs_mount                *mp,
+       uint                            nextents)
+
+{
+       struct xfs_cui_log_item         *cuip;
+
+       ASSERT(nextents > 0);
+       if (nextents > XFS_CUI_MAX_FAST_EXTENTS)
+               cuip = kmem_zalloc(xfs_cui_log_item_sizeof(nextents),
+                               KM_SLEEP);
+       else
+               cuip = kmem_zone_zalloc(xfs_cui_zone, KM_SLEEP);
+
+       xfs_log_item_init(mp, &cuip->cui_item, XFS_LI_CUI, &xfs_cui_item_ops);
+       cuip->cui_format.cui_nextents = nextents;
+       cuip->cui_format.cui_id = (uintptr_t)(void *)cuip;
+       atomic_set(&cuip->cui_next_extent, 0);
+       atomic_set(&cuip->cui_refcount, 2);
+
+       return cuip;
+}
+
+/*
+ * Freeing the CUI requires that we remove it from the AIL if it has already
+ * been placed there. However, the CUI may not yet have been placed in the AIL
+ * when called by xfs_cui_release() from CUD processing due to the ordering of
+ * committed vs unpin operations in bulk insert operations. Hence the reference
+ * count to ensure only the last caller frees the CUI.
+ */
+void
+xfs_cui_release(
+       struct xfs_cui_log_item *cuip)
+{
+       if (atomic_dec_and_test(&cuip->cui_refcount)) {
+               xfs_trans_ail_remove(&cuip->cui_item, SHUTDOWN_LOG_IO_ERROR);
+               xfs_cui_item_free(cuip);
+       }
+}
+
+static inline struct xfs_cud_log_item *CUD_ITEM(struct xfs_log_item *lip)
+{
+       return container_of(lip, struct xfs_cud_log_item, cud_item);
+}
+
+STATIC void
+xfs_cud_item_size(
+       struct xfs_log_item     *lip,
+       int                     *nvecs,
+       int                     *nbytes)
+{
+       *nvecs += 1;
+       *nbytes += sizeof(struct xfs_cud_log_format);
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given cud log item. We use only 1 iovec, and we point that
+ * at the cud_log_format structure embedded in the cud item.
+ * It is at this point that we assert that all of the extent
+ * slots in the cud item have been filled.
+ */
+STATIC void
+xfs_cud_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_vec      *lv)
+{
+       struct xfs_cud_log_item *cudp = CUD_ITEM(lip);
+       struct xfs_log_iovec    *vecp = NULL;
+
+       cudp->cud_format.cud_type = XFS_LI_CUD;
+       cudp->cud_format.cud_size = 1;
+
+       xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_CUD_FORMAT, &cudp->cud_format,
+                       sizeof(struct xfs_cud_log_format));
+}
+
+/*
+ * Pinning has no meaning for an cud item, so just return.
+ */
+STATIC void
+xfs_cud_item_pin(
+       struct xfs_log_item     *lip)
+{
+}
+
+/*
+ * Since pinning has no meaning for an cud item, unpinning does
+ * not either.
+ */
+STATIC void
+xfs_cud_item_unpin(
+       struct xfs_log_item     *lip,
+       int                     remove)
+{
+}
+
+/*
+ * There isn't much you can do to push on an cud item.  It is simply stuck
+ * waiting for the log to be flushed to disk.
+ */
+STATIC uint
+xfs_cud_item_push(
+       struct xfs_log_item     *lip,
+       struct list_head        *buffer_list)
+{
+       return XFS_ITEM_PINNED;
+}
+
+/*
+ * The CUD is either committed or aborted if the transaction is cancelled. If
+ * the transaction is cancelled, drop our reference to the CUI and free the
+ * CUD.
+ */
+STATIC void
+xfs_cud_item_unlock(
+       struct xfs_log_item     *lip)
+{
+       struct xfs_cud_log_item *cudp = CUD_ITEM(lip);
+
+       if (lip->li_flags & XFS_LI_ABORTED) {
+               xfs_cui_release(cudp->cud_cuip);
+               kmem_zone_free(xfs_cud_zone, cudp);
+       }
+}
+
+/*
+ * When the cud item is committed to disk, all we need to do is delete our
+ * reference to our partner cui item and then free ourselves. Since we're
+ * freeing ourselves we must return -1 to keep the transaction code from
+ * further referencing this item.
+ */
+STATIC xfs_lsn_t
+xfs_cud_item_committed(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+       struct xfs_cud_log_item *cudp = CUD_ITEM(lip);
+
+       /*
+        * Drop the CUI reference regardless of whether the CUD has been
+        * aborted. Once the CUD transaction is constructed, it is the sole
+        * responsibility of the CUD to release the CUI (even if the CUI is
+        * aborted due to log I/O error).
+        */
+       xfs_cui_release(cudp->cud_cuip);
+       kmem_zone_free(xfs_cud_zone, cudp);
+
+       return (xfs_lsn_t)-1;
+}
+
+/*
+ * The CUD dependency tracking op doesn't do squat.  It can't because
+ * it doesn't know where the free extent is coming from.  The dependency
+ * tracking has to be handled by the "enclosing" metadata object.  For
+ * example, for inodes, the inode is locked throughout the extent freeing
+ * so the dependency should be recorded there.
+ */
+STATIC void
+xfs_cud_item_committing(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+}
+
+/*
+ * This is the ops vector shared by all cud log items.
+ */
+static const struct xfs_item_ops xfs_cud_item_ops = {
+       .iop_size       = xfs_cud_item_size,
+       .iop_format     = xfs_cud_item_format,
+       .iop_pin        = xfs_cud_item_pin,
+       .iop_unpin      = xfs_cud_item_unpin,
+       .iop_unlock     = xfs_cud_item_unlock,
+       .iop_committed  = xfs_cud_item_committed,
+       .iop_push       = xfs_cud_item_push,
+       .iop_committing = xfs_cud_item_committing,
+};
+
+/*
+ * Allocate and initialize an cud item with the given number of extents.
+ */
+struct xfs_cud_log_item *
+xfs_cud_init(
+       struct xfs_mount                *mp,
+       struct xfs_cui_log_item         *cuip)
+
+{
+       struct xfs_cud_log_item *cudp;
+
+       cudp = kmem_zone_zalloc(xfs_cud_zone, KM_SLEEP);
+       xfs_log_item_init(mp, &cudp->cud_item, XFS_LI_CUD, &xfs_cud_item_ops);
+       cudp->cud_cuip = cuip;
+       cudp->cud_format.cud_cui_id = cuip->cui_format.cui_id;
+
+       return cudp;
+}
+
+/*
+ * Process a refcount update intent item that was recovered from the log.
+ * We need to update the refcountbt.
+ */
+int
+xfs_cui_recover(
+       struct xfs_mount                *mp,
+       struct xfs_cui_log_item         *cuip)
+{
+       int                             i;
+       int                             error = 0;
+       unsigned int                    refc_type;
+       struct xfs_phys_extent          *refc;
+       xfs_fsblock_t                   startblock_fsb;
+       bool                            op_ok;
+       struct xfs_cud_log_item         *cudp;
+       struct xfs_trans                *tp;
+       struct xfs_btree_cur            *rcur = NULL;
+       enum xfs_refcount_intent_type   type;
+       xfs_fsblock_t                   firstfsb;
+       xfs_fsblock_t                   new_fsb;
+       xfs_extlen_t                    new_len;
+       struct xfs_bmbt_irec            irec;
+       struct xfs_defer_ops            dfops;
+       bool                            requeue_only = false;
+
+       ASSERT(!test_bit(XFS_CUI_RECOVERED, &cuip->cui_flags));
+
+       /*
+        * First check the validity of the extents described by the
+        * CUI.  If any are bad, then assume that all are bad and
+        * just toss the CUI.
+        */
+       for (i = 0; i < cuip->cui_format.cui_nextents; i++) {
+               refc = &cuip->cui_format.cui_extents[i];
+               startblock_fsb = XFS_BB_TO_FSB(mp,
+                                  XFS_FSB_TO_DADDR(mp, refc->pe_startblock));
+               switch (refc->pe_flags & XFS_REFCOUNT_EXTENT_TYPE_MASK) {
+               case XFS_REFCOUNT_INCREASE:
+               case XFS_REFCOUNT_DECREASE:
+               case XFS_REFCOUNT_ALLOC_COW:
+               case XFS_REFCOUNT_FREE_COW:
+                       op_ok = true;
+                       break;
+               default:
+                       op_ok = false;
+                       break;
+               }
+               if (!op_ok || startblock_fsb == 0 ||
+                   refc->pe_len == 0 ||
+                   startblock_fsb >= mp->m_sb.sb_dblocks ||
+                   refc->pe_len >= mp->m_sb.sb_agblocks ||
+                   (refc->pe_flags & ~XFS_REFCOUNT_EXTENT_FLAGS)) {
+                       /*
+                        * This will pull the CUI from the AIL and
+                        * free the memory associated with it.
+                        */
+                       set_bit(XFS_CUI_RECOVERED, &cuip->cui_flags);
+                       xfs_cui_release(cuip);
+                       return -EIO;
+               }
+       }
+
+       /*
+        * Under normal operation, refcount updates are deferred, so we
+        * wouldn't be adding them directly to a transaction.  All
+        * refcount updates manage reservation usage internally and
+        * dynamically by deferring work that won't fit in the
+        * transaction.  Normally, any work that needs to be deferred
+        * gets attached to the same defer_ops that scheduled the
+        * refcount update.  However, we're in log recovery here, so we
+        * we create our own defer_ops and use that to finish up any
+        * work that doesn't fit.
+        */
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
+       if (error)
+               return error;
+       cudp = xfs_trans_get_cud(tp, cuip);
+
+       xfs_defer_init(&dfops, &firstfsb);
+       for (i = 0; i < cuip->cui_format.cui_nextents; i++) {
+               refc = &cuip->cui_format.cui_extents[i];
+               refc_type = refc->pe_flags & XFS_REFCOUNT_EXTENT_TYPE_MASK;
+               switch (refc_type) {
+               case XFS_REFCOUNT_INCREASE:
+               case XFS_REFCOUNT_DECREASE:
+               case XFS_REFCOUNT_ALLOC_COW:
+               case XFS_REFCOUNT_FREE_COW:
+                       type = refc_type;
+                       break;
+               default:
+                       error = -EFSCORRUPTED;
+                       goto abort_error;
+               }
+               if (requeue_only) {
+                       new_fsb = refc->pe_startblock;
+                       new_len = refc->pe_len;
+               } else
+                       error = xfs_trans_log_finish_refcount_update(tp, cudp,
+                               &dfops, type, refc->pe_startblock, refc->pe_len,
+                               &new_fsb, &new_len, &rcur);
+               if (error)
+                       goto abort_error;
+
+               /* Requeue what we didn't finish. */
+               if (new_len > 0) {
+                       irec.br_startblock = new_fsb;
+                       irec.br_blockcount = new_len;
+                       switch (type) {
+                       case XFS_REFCOUNT_INCREASE:
+                               error = xfs_refcount_increase_extent(
+                                               tp->t_mountp, &dfops, &irec);
+                               break;
+                       case XFS_REFCOUNT_DECREASE:
+                               error = xfs_refcount_decrease_extent(
+                                               tp->t_mountp, &dfops, &irec);
+                               break;
+                       case XFS_REFCOUNT_ALLOC_COW:
+                               error = xfs_refcount_alloc_cow_extent(
+                                               tp->t_mountp, &dfops,
+                                               irec.br_startblock,
+                                               irec.br_blockcount);
+                               break;
+                       case XFS_REFCOUNT_FREE_COW:
+                               error = xfs_refcount_free_cow_extent(
+                                               tp->t_mountp, &dfops,
+                                               irec.br_startblock,
+                                               irec.br_blockcount);
+                               break;
+                       default:
+                               ASSERT(0);
+                       }
+                       if (error)
+                               goto abort_error;
+                       requeue_only = true;
+               }
+       }
+
+       xfs_refcount_finish_one_cleanup(tp, rcur, error);
+       error = xfs_defer_finish(&tp, &dfops, NULL);
+       if (error)
+               goto abort_error;
+       set_bit(XFS_CUI_RECOVERED, &cuip->cui_flags);
+       error = xfs_trans_commit(tp);
+       return error;
+
+abort_error:
+       xfs_refcount_finish_one_cleanup(tp, rcur, error);
+       xfs_defer_cancel(&dfops);
+       xfs_trans_cancel(tp);
+       return error;
+}
diff --git a/fs/xfs/xfs_refcount_item.h b/fs/xfs/xfs_refcount_item.h
new file mode 100644 (file)
index 0000000..5b74ddd
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef        __XFS_REFCOUNT_ITEM_H__
+#define        __XFS_REFCOUNT_ITEM_H__
+
+/*
+ * There are (currently) two pairs of refcount btree redo item types:
+ * increase and decrease.  The log items for these are CUI (refcount
+ * update intent) and CUD (refcount update done).  The redo item type
+ * is encoded in the flags field of each xfs_map_extent.
+ *
+ * *I items should be recorded in the *first* of a series of rolled
+ * transactions, and the *D items should be recorded in the same
+ * transaction that records the associated refcountbt updates.
+ *
+ * Should the system crash after the commit of the first transaction
+ * but before the commit of the final transaction in a series, log
+ * recovery will use the redo information recorded by the intent items
+ * to replay the refcountbt metadata updates.
+ */
+
+/* kernel only CUI/CUD definitions */
+
+struct xfs_mount;
+struct kmem_zone;
+
+/*
+ * Max number of extents in fast allocation path.
+ */
+#define        XFS_CUI_MAX_FAST_EXTENTS        16
+
+/*
+ * Define CUI flag bits. Manipulated by set/clear/test_bit operators.
+ */
+#define        XFS_CUI_RECOVERED               1
+
+/*
+ * This is the "refcount update intent" log item.  It is used to log
+ * the fact that some reverse mappings need to change.  It is used in
+ * conjunction with the "refcount update done" log item described
+ * below.
+ *
+ * These log items follow the same rules as struct xfs_efi_log_item;
+ * see the comments about that structure (in xfs_extfree_item.h) for
+ * more details.
+ */
+struct xfs_cui_log_item {
+       struct xfs_log_item             cui_item;
+       atomic_t                        cui_refcount;
+       atomic_t                        cui_next_extent;
+       unsigned long                   cui_flags;      /* misc flags */
+       struct xfs_cui_log_format       cui_format;
+};
+
+static inline size_t
+xfs_cui_log_item_sizeof(
+       unsigned int            nr)
+{
+       return offsetof(struct xfs_cui_log_item, cui_format) +
+                       xfs_cui_log_format_sizeof(nr);
+}
+
+/*
+ * This is the "refcount update done" log item.  It is used to log the
+ * fact that some refcountbt updates mentioned in an earlier cui item
+ * have been performed.
+ */
+struct xfs_cud_log_item {
+       struct xfs_log_item             cud_item;
+       struct xfs_cui_log_item         *cud_cuip;
+       struct xfs_cud_log_format       cud_format;
+};
+
+extern struct kmem_zone        *xfs_cui_zone;
+extern struct kmem_zone        *xfs_cud_zone;
+
+struct xfs_cui_log_item *xfs_cui_init(struct xfs_mount *, uint);
+struct xfs_cud_log_item *xfs_cud_init(struct xfs_mount *,
+               struct xfs_cui_log_item *);
+void xfs_cui_item_free(struct xfs_cui_log_item *);
+void xfs_cui_release(struct xfs_cui_log_item *);
+int xfs_cui_recover(struct xfs_mount *mp, struct xfs_cui_log_item *cuip);
+
+#endif /* __XFS_REFCOUNT_ITEM_H__ */
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
new file mode 100644 (file)
index 0000000..5965e94
--- /dev/null
@@ -0,0 +1,1688 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_inode.h"
+#include "xfs_trans.h"
+#include "xfs_inode_item.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_util.h"
+#include "xfs_error.h"
+#include "xfs_dir2.h"
+#include "xfs_dir2_priv.h"
+#include "xfs_ioctl.h"
+#include "xfs_trace.h"
+#include "xfs_log.h"
+#include "xfs_icache.h"
+#include "xfs_pnfs.h"
+#include "xfs_btree.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_refcount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans_space.h"
+#include "xfs_bit.h"
+#include "xfs_alloc.h"
+#include "xfs_quota_defs.h"
+#include "xfs_quota.h"
+#include "xfs_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_reflink.h"
+#include "xfs_iomap.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_sb.h"
+#include "xfs_ag_resv.h"
+
+/*
+ * Copy on Write of Shared Blocks
+ *
+ * XFS must preserve "the usual" file semantics even when two files share
+ * the same physical blocks.  This means that a write to one file must not
+ * alter the blocks in a different file; the way that we'll do that is
+ * through the use of a copy-on-write mechanism.  At a high level, that
+ * means that when we want to write to a shared block, we allocate a new
+ * block, write the data to the new block, and if that succeeds we map the
+ * new block into the file.
+ *
+ * XFS provides a "delayed allocation" mechanism that defers the allocation
+ * of disk blocks to dirty-but-not-yet-mapped file blocks as long as
+ * possible.  This reduces fragmentation by enabling the filesystem to ask
+ * for bigger chunks less often, which is exactly what we want for CoW.
+ *
+ * The delalloc mechanism begins when the kernel wants to make a block
+ * writable (write_begin or page_mkwrite).  If the offset is not mapped, we
+ * create a delalloc mapping, which is a regular in-core extent, but without
+ * a real startblock.  (For delalloc mappings, the startblock encodes both
+ * a flag that this is a delalloc mapping, and a worst-case estimate of how
+ * many blocks might be required to put the mapping into the BMBT.)  delalloc
+ * mappings are a reservation against the free space in the filesystem;
+ * adjacent mappings can also be combined into fewer larger mappings.
+ *
+ * When dirty pages are being written out (typically in writepage), the
+ * delalloc reservations are converted into real mappings by allocating
+ * blocks and replacing the delalloc mapping with real ones.  A delalloc
+ * mapping can be replaced by several real ones if the free space is
+ * fragmented.
+ *
+ * We want to adapt the delalloc mechanism for copy-on-write, since the
+ * write paths are similar.  The first two steps (creating the reservation
+ * and allocating the blocks) are exactly the same as delalloc except that
+ * the mappings must be stored in a separate CoW fork because we do not want
+ * to disturb the mapping in the data fork until we're sure that the write
+ * succeeded.  IO completion in this case is the process of removing the old
+ * mapping from the data fork and moving the new mapping from the CoW fork to
+ * the data fork.  This will be discussed shortly.
+ *
+ * For now, unaligned directio writes will be bounced back to the page cache.
+ * Block-aligned directio writes will use the same mechanism as buffered
+ * writes.
+ *
+ * CoW remapping must be done after the data block write completes,
+ * because we don't want to destroy the old data fork map until we're sure
+ * the new block has been written.  Since the new mappings are kept in a
+ * separate fork, we can simply iterate these mappings to find the ones
+ * that cover the file blocks that we just CoW'd.  For each extent, simply
+ * unmap the corresponding range in the data fork, map the new range into
+ * the data fork, and remove the extent from the CoW fork.
+ *
+ * Since the remapping operation can be applied to an arbitrary file
+ * range, we record the need for the remap step as a flag in the ioend
+ * instead of declaring a new IO type.  This is required for direct io
+ * because we only have ioend for the whole dio, and we have to be able to
+ * remember the presence of unwritten blocks and CoW blocks with a single
+ * ioend structure.  Better yet, the more ground we can cover with one
+ * ioend, the better.
+ */
+
+/*
+ * Given an AG extent, find the lowest-numbered run of shared blocks
+ * within that range and return the range in fbno/flen.  If
+ * find_end_of_shared is true, return the longest contiguous extent of
+ * shared blocks.  If there are no shared extents, fbno and flen will
+ * be set to NULLAGBLOCK and 0, respectively.
+ */
+int
+xfs_reflink_find_shared(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       xfs_agblock_t           *fbno,
+       xfs_extlen_t            *flen,
+       bool                    find_end_of_shared)
+{
+       struct xfs_buf          *agbp;
+       struct xfs_btree_cur    *cur;
+       int                     error;
+
+       error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+       if (error)
+               return error;
+
+       cur = xfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL);
+
+       error = xfs_refcount_find_shared(cur, agbno, aglen, fbno, flen,
+                       find_end_of_shared);
+
+       xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
+       xfs_buf_relse(agbp);
+       return error;
+}
+
+/*
+ * Trim the mapping to the next block where there's a change in the
+ * shared/unshared status.  More specifically, this means that we
+ * find the lowest-numbered extent of shared blocks that coincides with
+ * the given block mapping.  If the shared extent overlaps the start of
+ * the mapping, trim the mapping to the end of the shared extent.  If
+ * the shared region intersects the mapping, trim the mapping to the
+ * start of the shared extent.  If there are no shared regions that
+ * overlap, just return the original extent.
+ */
+int
+xfs_reflink_trim_around_shared(
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *irec,
+       bool                    *shared,
+       bool                    *trimmed)
+{
+       xfs_agnumber_t          agno;
+       xfs_agblock_t           agbno;
+       xfs_extlen_t            aglen;
+       xfs_agblock_t           fbno;
+       xfs_extlen_t            flen;
+       int                     error = 0;
+
+       /* Holes, unwritten, and delalloc extents cannot be shared */
+       if (!xfs_is_reflink_inode(ip) ||
+           ISUNWRITTEN(irec) ||
+           irec->br_startblock == HOLESTARTBLOCK ||
+           irec->br_startblock == DELAYSTARTBLOCK) {
+               *shared = false;
+               return 0;
+       }
+
+       trace_xfs_reflink_trim_around_shared(ip, irec);
+
+       agno = XFS_FSB_TO_AGNO(ip->i_mount, irec->br_startblock);
+       agbno = XFS_FSB_TO_AGBNO(ip->i_mount, irec->br_startblock);
+       aglen = irec->br_blockcount;
+
+       error = xfs_reflink_find_shared(ip->i_mount, agno, agbno,
+                       aglen, &fbno, &flen, true);
+       if (error)
+               return error;
+
+       *shared = *trimmed = false;
+       if (fbno == NULLAGBLOCK) {
+               /* No shared blocks at all. */
+               return 0;
+       } else if (fbno == agbno) {
+               /*
+                * The start of this extent is shared.  Truncate the
+                * mapping at the end of the shared region so that a
+                * subsequent iteration starts at the start of the
+                * unshared region.
+                */
+               irec->br_blockcount = flen;
+               *shared = true;
+               if (flen != aglen)
+                       *trimmed = true;
+               return 0;
+       } else {
+               /*
+                * There's a shared extent midway through this extent.
+                * Truncate the mapping at the start of the shared
+                * extent so that a subsequent iteration starts at the
+                * start of the shared region.
+                */
+               irec->br_blockcount = fbno - agbno;
+               *trimmed = true;
+               return 0;
+       }
+}
+
+/* Create a CoW reservation for a range of blocks within a file. */
+static int
+__xfs_reflink_reserve_cow(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           *offset_fsb,
+       xfs_fileoff_t           end_fsb,
+       bool                    *skipped)
+{
+       struct xfs_bmbt_irec    got, prev, imap;
+       xfs_fileoff_t           orig_end_fsb;
+       int                     nimaps, eof = 0, error = 0;
+       bool                    shared = false, trimmed = false;
+       xfs_extnum_t            idx;
+       xfs_extlen_t            align;
+
+       /* Already reserved?  Skip the refcount btree access. */
+       xfs_bmap_search_extents(ip, *offset_fsb, XFS_COW_FORK, &eof, &idx,
+                       &got, &prev);
+       if (!eof && got.br_startoff <= *offset_fsb) {
+               end_fsb = orig_end_fsb = got.br_startoff + got.br_blockcount;
+               trace_xfs_reflink_cow_found(ip, &got);
+               goto done;
+       }
+
+       /* Read extent from the source file. */
+       nimaps = 1;
+       error = xfs_bmapi_read(ip, *offset_fsb, end_fsb - *offset_fsb,
+                       &imap, &nimaps, 0);
+       if (error)
+               goto out_unlock;
+       ASSERT(nimaps == 1);
+
+       /* Trim the mapping to the nearest shared extent boundary. */
+       error = xfs_reflink_trim_around_shared(ip, &imap, &shared, &trimmed);
+       if (error)
+               goto out_unlock;
+
+       end_fsb = orig_end_fsb = imap.br_startoff + imap.br_blockcount;
+
+       /* Not shared?  Just report the (potentially capped) extent. */
+       if (!shared) {
+               *skipped = true;
+               goto done;
+       }
+
+       /*
+        * Fork all the shared blocks from our write offset until the end of
+        * the extent.
+        */
+       error = xfs_qm_dqattach_locked(ip, 0);
+       if (error)
+               goto out_unlock;
+
+       align = xfs_eof_alignment(ip, xfs_get_cowextsz_hint(ip));
+       if (align)
+               end_fsb = roundup_64(end_fsb, align);
+
+retry:
+       error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, *offset_fsb,
+                       end_fsb - *offset_fsb, &got,
+                       &prev, &idx, eof);
+       switch (error) {
+       case 0:
+               break;
+       case -ENOSPC:
+       case -EDQUOT:
+               /* retry without any preallocation */
+               trace_xfs_reflink_cow_enospc(ip, &imap);
+               if (end_fsb != orig_end_fsb) {
+                       end_fsb = orig_end_fsb;
+                       goto retry;
+               }
+               /*FALLTHRU*/
+       default:
+               goto out_unlock;
+       }
+
+       if (end_fsb != orig_end_fsb)
+               xfs_inode_set_cowblocks_tag(ip);
+
+       trace_xfs_reflink_cow_alloc(ip, &got);
+done:
+       *offset_fsb = end_fsb;
+out_unlock:
+       return error;
+}
+
+/* Create a CoW reservation for part of a file. */
+int
+xfs_reflink_reserve_cow_range(
+       struct xfs_inode        *ip,
+       xfs_off_t               offset,
+       xfs_off_t               count)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_fileoff_t           offset_fsb, end_fsb;
+       bool                    skipped = false;
+       int                     error;
+
+       trace_xfs_reflink_reserve_cow_range(ip, offset, count);
+
+       offset_fsb = XFS_B_TO_FSBT(mp, offset);
+       end_fsb = XFS_B_TO_FSB(mp, offset + count);
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       while (offset_fsb < end_fsb) {
+               error = __xfs_reflink_reserve_cow(ip, &offset_fsb, end_fsb,
+                               &skipped);
+               if (error) {
+                       trace_xfs_reflink_reserve_cow_range_error(ip, error,
+                               _RET_IP_);
+                       break;
+               }
+       }
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+       return error;
+}
+
+/* Allocate all CoW reservations covering a range of blocks in a file. */
+static int
+__xfs_reflink_allocate_cow(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           *offset_fsb,
+       xfs_fileoff_t           end_fsb)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_bmbt_irec    imap;
+       struct xfs_defer_ops    dfops;
+       struct xfs_trans        *tp;
+       xfs_fsblock_t           first_block;
+       xfs_fileoff_t           next_fsb;
+       int                     nimaps = 1, error;
+       bool                    skipped = false;
+
+       xfs_defer_init(&dfops, &first_block);
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0,
+                       XFS_TRANS_RESERVE, &tp);
+       if (error)
+               return error;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+       next_fsb = *offset_fsb;
+       error = __xfs_reflink_reserve_cow(ip, &next_fsb, end_fsb, &skipped);
+       if (error)
+               goto out_trans_cancel;
+
+       if (skipped) {
+               *offset_fsb = next_fsb;
+               goto out_trans_cancel;
+       }
+
+       xfs_trans_ijoin(tp, ip, 0);
+       error = xfs_bmapi_write(tp, ip, *offset_fsb, next_fsb - *offset_fsb,
+                       XFS_BMAPI_COWFORK, &first_block,
+                       XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK),
+                       &imap, &nimaps, &dfops);
+       if (error)
+               goto out_trans_cancel;
+
+       /* We might not have been able to map the whole delalloc extent */
+       *offset_fsb = min(*offset_fsb + imap.br_blockcount, next_fsb);
+
+       error = xfs_defer_finish(&tp, &dfops, NULL);
+       if (error)
+               goto out_trans_cancel;
+
+       error = xfs_trans_commit(tp);
+
+out_unlock:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return error;
+out_trans_cancel:
+       xfs_defer_cancel(&dfops);
+       xfs_trans_cancel(tp);
+       goto out_unlock;
+}
+
+/* Allocate all CoW reservations covering a part of a file. */
+int
+xfs_reflink_allocate_cow_range(
+       struct xfs_inode        *ip,
+       xfs_off_t               offset,
+       xfs_off_t               count)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_fileoff_t           offset_fsb = XFS_B_TO_FSBT(mp, offset);
+       xfs_fileoff_t           end_fsb = XFS_B_TO_FSB(mp, offset + count);
+       int                     error;
+
+       ASSERT(xfs_is_reflink_inode(ip));
+
+       trace_xfs_reflink_allocate_cow_range(ip, offset, count);
+
+       /*
+        * Make sure that the dquots are there.
+        */
+       error = xfs_qm_dqattach(ip, 0);
+       if (error)
+               return error;
+
+       while (offset_fsb < end_fsb) {
+               error = __xfs_reflink_allocate_cow(ip, &offset_fsb, end_fsb);
+               if (error) {
+                       trace_xfs_reflink_allocate_cow_range_error(ip, error,
+                                       _RET_IP_);
+                       break;
+               }
+       }
+
+       return error;
+}
+
+/*
+ * Find the CoW reservation (and whether or not it needs block allocation)
+ * for a given byte offset of a file.
+ */
+bool
+xfs_reflink_find_cow_mapping(
+       struct xfs_inode                *ip,
+       xfs_off_t                       offset,
+       struct xfs_bmbt_irec            *imap,
+       bool                            *need_alloc)
+{
+       struct xfs_bmbt_irec            irec;
+       struct xfs_ifork                *ifp;
+       struct xfs_bmbt_rec_host        *gotp;
+       xfs_fileoff_t                   bno;
+       xfs_extnum_t                    idx;
+
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
+       ASSERT(xfs_is_reflink_inode(ip));
+
+       /* Find the extent in the CoW fork. */
+       ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+       bno = XFS_B_TO_FSBT(ip->i_mount, offset);
+       gotp = xfs_iext_bno_to_ext(ifp, bno, &idx);
+       if (!gotp)
+               return false;
+
+       xfs_bmbt_get_all(gotp, &irec);
+       if (bno >= irec.br_startoff + irec.br_blockcount ||
+           bno < irec.br_startoff)
+               return false;
+
+       trace_xfs_reflink_find_cow_mapping(ip, offset, 1, XFS_IO_OVERWRITE,
+                       &irec);
+
+       /* If it's still delalloc, we must allocate later. */
+       *imap = irec;
+       *need_alloc = !!(isnullstartblock(irec.br_startblock));
+
+       return true;
+}
+
+/*
+ * Trim an extent to end at the next CoW reservation past offset_fsb.
+ */
+int
+xfs_reflink_trim_irec_to_next_cow(
+       struct xfs_inode                *ip,
+       xfs_fileoff_t                   offset_fsb,
+       struct xfs_bmbt_irec            *imap)
+{
+       struct xfs_bmbt_irec            irec;
+       struct xfs_ifork                *ifp;
+       struct xfs_bmbt_rec_host        *gotp;
+       xfs_extnum_t                    idx;
+
+       if (!xfs_is_reflink_inode(ip))
+               return 0;
+
+       /* Find the extent in the CoW fork. */
+       ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+       gotp = xfs_iext_bno_to_ext(ifp, offset_fsb, &idx);
+       if (!gotp)
+               return 0;
+       xfs_bmbt_get_all(gotp, &irec);
+
+       /* This is the extent before; try sliding up one. */
+       if (irec.br_startoff < offset_fsb) {
+               idx++;
+               if (idx >= ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
+                       return 0;
+               gotp = xfs_iext_get_ext(ifp, idx);
+               xfs_bmbt_get_all(gotp, &irec);
+       }
+
+       if (irec.br_startoff >= imap->br_startoff + imap->br_blockcount)
+               return 0;
+
+       imap->br_blockcount = irec.br_startoff - imap->br_startoff;
+       trace_xfs_reflink_trim_irec(ip, imap);
+
+       return 0;
+}
+
+/*
+ * Cancel all pending CoW reservations for some block range of an inode.
+ */
+int
+xfs_reflink_cancel_cow_blocks(
+       struct xfs_inode                *ip,
+       struct xfs_trans                **tpp,
+       xfs_fileoff_t                   offset_fsb,
+       xfs_fileoff_t                   end_fsb)
+{
+       struct xfs_bmbt_irec            irec;
+       xfs_filblks_t                   count_fsb;
+       xfs_fsblock_t                   firstfsb;
+       struct xfs_defer_ops            dfops;
+       int                             error = 0;
+       int                             nimaps;
+
+       if (!xfs_is_reflink_inode(ip))
+               return 0;
+
+       /* Go find the old extent in the CoW fork. */
+       while (offset_fsb < end_fsb) {
+               nimaps = 1;
+               count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb);
+               error = xfs_bmapi_read(ip, offset_fsb, count_fsb, &irec,
+                               &nimaps, XFS_BMAPI_COWFORK);
+               if (error)
+                       break;
+               ASSERT(nimaps == 1);
+
+               trace_xfs_reflink_cancel_cow(ip, &irec);
+
+               if (irec.br_startblock == DELAYSTARTBLOCK) {
+                       /* Free a delayed allocation. */
+                       xfs_mod_fdblocks(ip->i_mount, irec.br_blockcount,
+                                       false);
+                       ip->i_delayed_blks -= irec.br_blockcount;
+
+                       /* Remove the mapping from the CoW fork. */
+                       error = xfs_bunmapi_cow(ip, &irec);
+                       if (error)
+                               break;
+               } else if (irec.br_startblock == HOLESTARTBLOCK) {
+                       /* empty */
+               } else {
+                       xfs_trans_ijoin(*tpp, ip, 0);
+                       xfs_defer_init(&dfops, &firstfsb);
+
+                       /* Free the CoW orphan record. */
+                       error = xfs_refcount_free_cow_extent(ip->i_mount,
+                                       &dfops, irec.br_startblock,
+                                       irec.br_blockcount);
+                       if (error)
+                               break;
+
+                       xfs_bmap_add_free(ip->i_mount, &dfops,
+                                       irec.br_startblock, irec.br_blockcount,
+                                       NULL);
+
+                       /* Update quota accounting */
+                       xfs_trans_mod_dquot_byino(*tpp, ip, XFS_TRANS_DQ_BCOUNT,
+                                       -(long)irec.br_blockcount);
+
+                       /* Roll the transaction */
+                       error = xfs_defer_finish(tpp, &dfops, ip);
+                       if (error) {
+                               xfs_defer_cancel(&dfops);
+                               break;
+                       }
+
+                       /* Remove the mapping from the CoW fork. */
+                       error = xfs_bunmapi_cow(ip, &irec);
+                       if (error)
+                               break;
+               }
+
+               /* Roll on... */
+               offset_fsb = irec.br_startoff + irec.br_blockcount;
+       }
+
+       return error;
+}
+
+/*
+ * Cancel all pending CoW reservations for some byte range of an inode.
+ */
+int
+xfs_reflink_cancel_cow_range(
+       struct xfs_inode        *ip,
+       xfs_off_t               offset,
+       xfs_off_t               count)
+{
+       struct xfs_trans        *tp;
+       xfs_fileoff_t           offset_fsb;
+       xfs_fileoff_t           end_fsb;
+       int                     error;
+
+       trace_xfs_reflink_cancel_cow_range(ip, offset, count);
+       ASSERT(xfs_is_reflink_inode(ip));
+
+       offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
+       if (count == NULLFILEOFF)
+               end_fsb = NULLFILEOFF;
+       else
+               end_fsb = XFS_B_TO_FSB(ip->i_mount, offset + count);
+
+       /* Start a rolling transaction to remove the mappings */
+       error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_write,
+                       0, 0, 0, &tp);
+       if (error)
+               goto out;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
+       /* Scrape out the old CoW reservations */
+       error = xfs_reflink_cancel_cow_blocks(ip, &tp, offset_fsb, end_fsb);
+       if (error)
+               goto out_cancel;
+
+       error = xfs_trans_commit(tp);
+
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return error;
+
+out_cancel:
+       xfs_trans_cancel(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out:
+       trace_xfs_reflink_cancel_cow_range_error(ip, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Remap parts of a file's data fork after a successful CoW.
+ */
+int
+xfs_reflink_end_cow(
+       struct xfs_inode                *ip,
+       xfs_off_t                       offset,
+       xfs_off_t                       count)
+{
+       struct xfs_bmbt_irec            irec;
+       struct xfs_bmbt_irec            uirec;
+       struct xfs_trans                *tp;
+       xfs_fileoff_t                   offset_fsb;
+       xfs_fileoff_t                   end_fsb;
+       xfs_filblks_t                   count_fsb;
+       xfs_fsblock_t                   firstfsb;
+       struct xfs_defer_ops            dfops;
+       int                             error;
+       unsigned int                    resblks;
+       xfs_filblks_t                   ilen;
+       xfs_filblks_t                   rlen;
+       int                             nimaps;
+
+       trace_xfs_reflink_end_cow(ip, offset, count);
+
+       offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
+       end_fsb = XFS_B_TO_FSB(ip->i_mount, offset + count);
+       count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb);
+
+       /* Start a rolling transaction to switch the mappings */
+       resblks = XFS_EXTENTADD_SPACE_RES(ip->i_mount, XFS_DATA_FORK);
+       error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_write,
+                       resblks, 0, 0, &tp);
+       if (error)
+               goto out;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
+       /* Go find the old extent in the CoW fork. */
+       while (offset_fsb < end_fsb) {
+               /* Read extent from the source file */
+               nimaps = 1;
+               count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb);
+               error = xfs_bmapi_read(ip, offset_fsb, count_fsb, &irec,
+                               &nimaps, XFS_BMAPI_COWFORK);
+               if (error)
+                       goto out_cancel;
+               ASSERT(nimaps == 1);
+
+               ASSERT(irec.br_startblock != DELAYSTARTBLOCK);
+               trace_xfs_reflink_cow_remap(ip, &irec);
+
+               /*
+                * We can have a hole in the CoW fork if part of a directio
+                * write is CoW but part of it isn't.
+                */
+               rlen = ilen = irec.br_blockcount;
+               if (irec.br_startblock == HOLESTARTBLOCK)
+                       goto next_extent;
+
+               /* Unmap the old blocks in the data fork. */
+               while (rlen) {
+                       xfs_defer_init(&dfops, &firstfsb);
+                       error = __xfs_bunmapi(tp, ip, irec.br_startoff,
+                                       &rlen, 0, 1, &firstfsb, &dfops);
+                       if (error)
+                               goto out_defer;
+
+                       /*
+                        * Trim the extent to whatever got unmapped.
+                        * Remember, bunmapi works backwards.
+                        */
+                       uirec.br_startblock = irec.br_startblock + rlen;
+                       uirec.br_startoff = irec.br_startoff + rlen;
+                       uirec.br_blockcount = irec.br_blockcount - rlen;
+                       irec.br_blockcount = rlen;
+                       trace_xfs_reflink_cow_remap_piece(ip, &uirec);
+
+                       /* Free the CoW orphan record. */
+                       error = xfs_refcount_free_cow_extent(tp->t_mountp,
+                                       &dfops, uirec.br_startblock,
+                                       uirec.br_blockcount);
+                       if (error)
+                               goto out_defer;
+
+                       /* Map the new blocks into the data fork. */
+                       error = xfs_bmap_map_extent(tp->t_mountp, &dfops,
+                                       ip, &uirec);
+                       if (error)
+                               goto out_defer;
+
+                       /* Remove the mapping from the CoW fork. */
+                       error = xfs_bunmapi_cow(ip, &uirec);
+                       if (error)
+                               goto out_defer;
+
+                       error = xfs_defer_finish(&tp, &dfops, ip);
+                       if (error)
+                               goto out_defer;
+               }
+
+next_extent:
+               /* Roll on... */
+               offset_fsb = irec.br_startoff + ilen;
+       }
+
+       error = xfs_trans_commit(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       if (error)
+               goto out;
+       return 0;
+
+out_defer:
+       xfs_defer_cancel(&dfops);
+out_cancel:
+       xfs_trans_cancel(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out:
+       trace_xfs_reflink_end_cow_error(ip, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Free leftover CoW reservations that didn't get cleaned out.
+ */
+int
+xfs_reflink_recover_cow(
+       struct xfs_mount        *mp)
+{
+       xfs_agnumber_t          agno;
+       int                     error = 0;
+
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+               error = xfs_refcount_recover_cow_leftovers(mp, agno);
+               if (error)
+                       break;
+       }
+
+       return error;
+}
+
+/*
+ * Reflinking (Block) Ranges of Two Files Together
+ *
+ * First, ensure that the reflink flag is set on both inodes.  The flag is an
+ * optimization to avoid unnecessary refcount btree lookups in the write path.
+ *
+ * Now we can iteratively remap the range of extents (and holes) in src to the
+ * corresponding ranges in dest.  Let drange and srange denote the ranges of
+ * logical blocks in dest and src touched by the reflink operation.
+ *
+ * While the length of drange is greater than zero,
+ *    - Read src's bmbt at the start of srange ("imap")
+ *    - If imap doesn't exist, make imap appear to start at the end of srange
+ *      with zero length.
+ *    - If imap starts before srange, advance imap to start at srange.
+ *    - If imap goes beyond srange, truncate imap to end at the end of srange.
+ *    - Punch (imap start - srange start + imap len) blocks from dest at
+ *      offset (drange start).
+ *    - If imap points to a real range of pblks,
+ *         > Increase the refcount of the imap's pblks
+ *         > Map imap's pblks into dest at the offset
+ *           (drange start + imap start - srange start)
+ *    - Advance drange and srange by (imap start - srange start + imap len)
+ *
+ * Finally, if the reflink made dest longer, update both the in-core and
+ * on-disk file sizes.
+ *
+ * ASCII Art Demonstration:
+ *
+ * Let's say we want to reflink this source file:
+ *
+ * ----SSSSSSS-SSSSS----SSSSSS (src file)
+ *   <-------------------->
+ *
+ * into this destination file:
+ *
+ * --DDDDDDDDDDDDDDDDDDD--DDD (dest file)
+ *        <-------------------->
+ * '-' means a hole, and 'S' and 'D' are written blocks in the src and dest.
+ * Observe that the range has different logical offsets in either file.
+ *
+ * Consider that the first extent in the source file doesn't line up with our
+ * reflink range.  Unmapping  and remapping are separate operations, so we can
+ * unmap more blocks from the destination file than we remap.
+ *
+ * ----SSSSSSS-SSSSS----SSSSSS
+ *   <------->
+ * --DDDDD---------DDDDD--DDD
+ *        <------->
+ *
+ * Now remap the source extent into the destination file:
+ *
+ * ----SSSSSSS-SSSSS----SSSSSS
+ *   <------->
+ * --DDDDD--SSSSSSSDDDDD--DDD
+ *        <------->
+ *
+ * Do likewise with the second hole and extent in our range.  Holes in the
+ * unmap range don't affect our operation.
+ *
+ * ----SSSSSSS-SSSSS----SSSSSS
+ *            <---->
+ * --DDDDD--SSSSSSS-SSSSS-DDD
+ *                 <---->
+ *
+ * Finally, unmap and remap part of the third extent.  This will increase the
+ * size of the destination file.
+ *
+ * ----SSSSSSS-SSSSS----SSSSSS
+ *                  <----->
+ * --DDDDD--SSSSSSS-SSSSS----SSS
+ *                       <----->
+ *
+ * Once we update the destination file's i_size, we're done.
+ */
+
+/*
+ * Ensure the reflink bit is set in both inodes.
+ */
+STATIC int
+xfs_reflink_set_inode_flag(
+       struct xfs_inode        *src,
+       struct xfs_inode        *dest)
+{
+       struct xfs_mount        *mp = src->i_mount;
+       int                     error;
+       struct xfs_trans        *tp;
+
+       if (xfs_is_reflink_inode(src) && xfs_is_reflink_inode(dest))
+               return 0;
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
+       if (error)
+               goto out_error;
+
+       /* Lock both files against IO */
+       if (src->i_ino == dest->i_ino)
+               xfs_ilock(src, XFS_ILOCK_EXCL);
+       else
+               xfs_lock_two_inodes(src, dest, XFS_ILOCK_EXCL);
+
+       if (!xfs_is_reflink_inode(src)) {
+               trace_xfs_reflink_set_inode_flag(src);
+               xfs_trans_ijoin(tp, src, XFS_ILOCK_EXCL);
+               src->i_d.di_flags2 |= XFS_DIFLAG2_REFLINK;
+               xfs_trans_log_inode(tp, src, XFS_ILOG_CORE);
+               xfs_ifork_init_cow(src);
+       } else
+               xfs_iunlock(src, XFS_ILOCK_EXCL);
+
+       if (src->i_ino == dest->i_ino)
+               goto commit_flags;
+
+       if (!xfs_is_reflink_inode(dest)) {
+               trace_xfs_reflink_set_inode_flag(dest);
+               xfs_trans_ijoin(tp, dest, XFS_ILOCK_EXCL);
+               dest->i_d.di_flags2 |= XFS_DIFLAG2_REFLINK;
+               xfs_trans_log_inode(tp, dest, XFS_ILOG_CORE);
+               xfs_ifork_init_cow(dest);
+       } else
+               xfs_iunlock(dest, XFS_ILOCK_EXCL);
+
+commit_flags:
+       error = xfs_trans_commit(tp);
+       if (error)
+               goto out_error;
+       return error;
+
+out_error:
+       trace_xfs_reflink_set_inode_flag_error(dest, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Update destination inode size & cowextsize hint, if necessary.
+ */
+STATIC int
+xfs_reflink_update_dest(
+       struct xfs_inode        *dest,
+       xfs_off_t               newlen,
+       xfs_extlen_t            cowextsize)
+{
+       struct xfs_mount        *mp = dest->i_mount;
+       struct xfs_trans        *tp;
+       int                     error;
+
+       if (newlen <= i_size_read(VFS_I(dest)) && cowextsize == 0)
+               return 0;
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
+       if (error)
+               goto out_error;
+
+       xfs_ilock(dest, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, dest, XFS_ILOCK_EXCL);
+
+       if (newlen > i_size_read(VFS_I(dest))) {
+               trace_xfs_reflink_update_inode_size(dest, newlen);
+               i_size_write(VFS_I(dest), newlen);
+               dest->i_d.di_size = newlen;
+       }
+
+       if (cowextsize) {
+               dest->i_d.di_cowextsize = cowextsize;
+               dest->i_d.di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
+       }
+
+       xfs_trans_log_inode(tp, dest, XFS_ILOG_CORE);
+
+       error = xfs_trans_commit(tp);
+       if (error)
+               goto out_error;
+       return error;
+
+out_error:
+       trace_xfs_reflink_update_inode_size_error(dest, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Do we have enough reserve in this AG to handle a reflink?  The refcount
+ * btree already reserved all the space it needs, but the rmap btree can grow
+ * infinitely, so we won't allow more reflinks when the AG is down to the
+ * btree reserves.
+ */
+static int
+xfs_reflink_ag_has_free_space(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno)
+{
+       struct xfs_perag        *pag;
+       int                     error = 0;
+
+       if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
+               return 0;
+
+       pag = xfs_perag_get(mp, agno);
+       if (xfs_ag_resv_critical(pag, XFS_AG_RESV_AGFL) ||
+           xfs_ag_resv_critical(pag, XFS_AG_RESV_METADATA))
+               error = -ENOSPC;
+       xfs_perag_put(pag);
+       return error;
+}
+
+/*
+ * Unmap a range of blocks from a file, then map other blocks into the hole.
+ * The range to unmap is (destoff : destoff + srcioff + irec->br_blockcount).
+ * The extent irec is mapped into dest at irec->br_startoff.
+ */
+STATIC int
+xfs_reflink_remap_extent(
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *irec,
+       xfs_fileoff_t           destoff,
+       xfs_off_t               new_isize)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       xfs_fsblock_t           firstfsb;
+       unsigned int            resblks;
+       struct xfs_defer_ops    dfops;
+       struct xfs_bmbt_irec    uirec;
+       bool                    real_extent;
+       xfs_filblks_t           rlen;
+       xfs_filblks_t           unmap_len;
+       xfs_off_t               newlen;
+       int                     error;
+
+       unmap_len = irec->br_startoff + irec->br_blockcount - destoff;
+       trace_xfs_reflink_punch_range(ip, destoff, unmap_len);
+
+       /* Only remap normal extents. */
+       real_extent =  (irec->br_startblock != HOLESTARTBLOCK &&
+                       irec->br_startblock != DELAYSTARTBLOCK &&
+                       !ISUNWRITTEN(irec));
+
+       /* No reflinking if we're low on space */
+       if (real_extent) {
+               error = xfs_reflink_ag_has_free_space(mp,
+                               XFS_FSB_TO_AGNO(mp, irec->br_startblock));
+               if (error)
+                       goto out;
+       }
+
+       /* Start a rolling transaction to switch the mappings */
+       resblks = XFS_EXTENTADD_SPACE_RES(ip->i_mount, XFS_DATA_FORK);
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
+       if (error)
+               goto out;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
+       /* If we're not just clearing space, then do we have enough quota? */
+       if (real_extent) {
+               error = xfs_trans_reserve_quota_nblks(tp, ip,
+                               irec->br_blockcount, 0, XFS_QMOPT_RES_REGBLKS);
+               if (error)
+                       goto out_cancel;
+       }
+
+       trace_xfs_reflink_remap(ip, irec->br_startoff,
+                               irec->br_blockcount, irec->br_startblock);
+
+       /* Unmap the old blocks in the data fork. */
+       rlen = unmap_len;
+       while (rlen) {
+               xfs_defer_init(&dfops, &firstfsb);
+               error = __xfs_bunmapi(tp, ip, destoff, &rlen, 0, 1,
+                               &firstfsb, &dfops);
+               if (error)
+                       goto out_defer;
+
+               /*
+                * Trim the extent to whatever got unmapped.
+                * Remember, bunmapi works backwards.
+                */
+               uirec.br_startblock = irec->br_startblock + rlen;
+               uirec.br_startoff = irec->br_startoff + rlen;
+               uirec.br_blockcount = unmap_len - rlen;
+               unmap_len = rlen;
+
+               /* If this isn't a real mapping, we're done. */
+               if (!real_extent || uirec.br_blockcount == 0)
+                       goto next_extent;
+
+               trace_xfs_reflink_remap(ip, uirec.br_startoff,
+                               uirec.br_blockcount, uirec.br_startblock);
+
+               /* Update the refcount tree */
+               error = xfs_refcount_increase_extent(mp, &dfops, &uirec);
+               if (error)
+                       goto out_defer;
+
+               /* Map the new blocks into the data fork. */
+               error = xfs_bmap_map_extent(mp, &dfops, ip, &uirec);
+               if (error)
+                       goto out_defer;
+
+               /* Update quota accounting. */
+               xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT,
+                               uirec.br_blockcount);
+
+               /* Update dest isize if needed. */
+               newlen = XFS_FSB_TO_B(mp,
+                               uirec.br_startoff + uirec.br_blockcount);
+               newlen = min_t(xfs_off_t, newlen, new_isize);
+               if (newlen > i_size_read(VFS_I(ip))) {
+                       trace_xfs_reflink_update_inode_size(ip, newlen);
+                       i_size_write(VFS_I(ip), newlen);
+                       ip->i_d.di_size = newlen;
+                       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+               }
+
+next_extent:
+               /* Process all the deferred stuff. */
+               error = xfs_defer_finish(&tp, &dfops, ip);
+               if (error)
+                       goto out_defer;
+       }
+
+       error = xfs_trans_commit(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       if (error)
+               goto out;
+       return 0;
+
+out_defer:
+       xfs_defer_cancel(&dfops);
+out_cancel:
+       xfs_trans_cancel(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out:
+       trace_xfs_reflink_remap_extent_error(ip, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Iteratively remap one file's extents (and holes) to another's.
+ */
+STATIC int
+xfs_reflink_remap_blocks(
+       struct xfs_inode        *src,
+       xfs_fileoff_t           srcoff,
+       struct xfs_inode        *dest,
+       xfs_fileoff_t           destoff,
+       xfs_filblks_t           len,
+       xfs_off_t               new_isize)
+{
+       struct xfs_bmbt_irec    imap;
+       int                     nimaps;
+       int                     error = 0;
+       xfs_filblks_t           range_len;
+
+       /* drange = (destoff, destoff + len); srange = (srcoff, srcoff + len) */
+       while (len) {
+               trace_xfs_reflink_remap_blocks_loop(src, srcoff, len,
+                               dest, destoff);
+               /* Read extent from the source file */
+               nimaps = 1;
+               xfs_ilock(src, XFS_ILOCK_EXCL);
+               error = xfs_bmapi_read(src, srcoff, len, &imap, &nimaps, 0);
+               xfs_iunlock(src, XFS_ILOCK_EXCL);
+               if (error)
+                       goto err;
+               ASSERT(nimaps == 1);
+
+               trace_xfs_reflink_remap_imap(src, srcoff, len, XFS_IO_OVERWRITE,
+                               &imap);
+
+               /* Translate imap into the destination file. */
+               range_len = imap.br_startoff + imap.br_blockcount - srcoff;
+               imap.br_startoff += destoff - srcoff;
+
+               /* Clear dest from destoff to the end of imap and map it in. */
+               error = xfs_reflink_remap_extent(dest, &imap, destoff,
+                               new_isize);
+               if (error)
+                       goto err;
+
+               if (fatal_signal_pending(current)) {
+                       error = -EINTR;
+                       goto err;
+               }
+
+               /* Advance drange/srange */
+               srcoff += range_len;
+               destoff += range_len;
+               len -= range_len;
+       }
+
+       return 0;
+
+err:
+       trace_xfs_reflink_remap_blocks_error(dest, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Read a page's worth of file data into the page cache.  Return the page
+ * locked.
+ */
+static struct page *
+xfs_get_page(
+       struct inode    *inode,
+       xfs_off_t       offset)
+{
+       struct address_space    *mapping;
+       struct page             *page;
+       pgoff_t                 n;
+
+       n = offset >> PAGE_SHIFT;
+       mapping = inode->i_mapping;
+       page = read_mapping_page(mapping, n, NULL);
+       if (IS_ERR(page))
+               return page;
+       if (!PageUptodate(page)) {
+               put_page(page);
+               return ERR_PTR(-EIO);
+       }
+       lock_page(page);
+       return page;
+}
+
+/*
+ * Compare extents of two files to see if they are the same.
+ */
+static int
+xfs_compare_extents(
+       struct inode    *src,
+       xfs_off_t       srcoff,
+       struct inode    *dest,
+       xfs_off_t       destoff,
+       xfs_off_t       len,
+       bool            *is_same)
+{
+       xfs_off_t       src_poff;
+       xfs_off_t       dest_poff;
+       void            *src_addr;
+       void            *dest_addr;
+       struct page     *src_page;
+       struct page     *dest_page;
+       xfs_off_t       cmp_len;
+       bool            same;
+       int             error;
+
+       error = -EINVAL;
+       same = true;
+       while (len) {
+               src_poff = srcoff & (PAGE_SIZE - 1);
+               dest_poff = destoff & (PAGE_SIZE - 1);
+               cmp_len = min(PAGE_SIZE - src_poff,
+                             PAGE_SIZE - dest_poff);
+               cmp_len = min(cmp_len, len);
+               ASSERT(cmp_len > 0);
+
+               trace_xfs_reflink_compare_extents(XFS_I(src), srcoff, cmp_len,
+                               XFS_I(dest), destoff);
+
+               src_page = xfs_get_page(src, srcoff);
+               if (IS_ERR(src_page)) {
+                       error = PTR_ERR(src_page);
+                       goto out_error;
+               }
+               dest_page = xfs_get_page(dest, destoff);
+               if (IS_ERR(dest_page)) {
+                       error = PTR_ERR(dest_page);
+                       unlock_page(src_page);
+                       put_page(src_page);
+                       goto out_error;
+               }
+               src_addr = kmap_atomic(src_page);
+               dest_addr = kmap_atomic(dest_page);
+
+               flush_dcache_page(src_page);
+               flush_dcache_page(dest_page);
+
+               if (memcmp(src_addr + src_poff, dest_addr + dest_poff, cmp_len))
+                       same = false;
+
+               kunmap_atomic(dest_addr);
+               kunmap_atomic(src_addr);
+               unlock_page(dest_page);
+               unlock_page(src_page);
+               put_page(dest_page);
+               put_page(src_page);
+
+               if (!same)
+                       break;
+
+               srcoff += cmp_len;
+               destoff += cmp_len;
+               len -= cmp_len;
+       }
+
+       *is_same = same;
+       return 0;
+
+out_error:
+       trace_xfs_reflink_compare_extents_error(XFS_I(dest), error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Link a range of blocks from one file to another.
+ */
+int
+xfs_reflink_remap_range(
+       struct xfs_inode        *src,
+       xfs_off_t               srcoff,
+       struct xfs_inode        *dest,
+       xfs_off_t               destoff,
+       xfs_off_t               len,
+       unsigned int            flags)
+{
+       struct xfs_mount        *mp = src->i_mount;
+       xfs_fileoff_t           sfsbno, dfsbno;
+       xfs_filblks_t           fsblen;
+       int                     error;
+       xfs_extlen_t            cowextsize;
+       bool                    is_same;
+
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return -EOPNOTSUPP;
+
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return -EIO;
+
+       /* Don't reflink realtime inodes */
+       if (XFS_IS_REALTIME_INODE(src) || XFS_IS_REALTIME_INODE(dest))
+               return -EINVAL;
+
+       if (flags & ~XFS_REFLINK_ALL)
+               return -EINVAL;
+
+       trace_xfs_reflink_remap_range(src, srcoff, len, dest, destoff);
+
+       /* Lock both files against IO */
+       if (src->i_ino == dest->i_ino) {
+               xfs_ilock(src, XFS_IOLOCK_EXCL);
+               xfs_ilock(src, XFS_MMAPLOCK_EXCL);
+       } else {
+               xfs_lock_two_inodes(src, dest, XFS_IOLOCK_EXCL);
+               xfs_lock_two_inodes(src, dest, XFS_MMAPLOCK_EXCL);
+       }
+
+       /*
+        * Check that the extents are the same.
+        */
+       if (flags & XFS_REFLINK_DEDUPE) {
+               is_same = false;
+               error = xfs_compare_extents(VFS_I(src), srcoff, VFS_I(dest),
+                               destoff, len, &is_same);
+               if (error)
+                       goto out_error;
+               if (!is_same) {
+                       error = -EBADE;
+                       goto out_error;
+               }
+       }
+
+       error = xfs_reflink_set_inode_flag(src, dest);
+       if (error)
+               goto out_error;
+
+       /*
+        * Invalidate the page cache so that we can clear any CoW mappings
+        * in the destination file.
+        */
+       truncate_inode_pages_range(&VFS_I(dest)->i_data, destoff,
+                                  PAGE_ALIGN(destoff + len) - 1);
+
+       dfsbno = XFS_B_TO_FSBT(mp, destoff);
+       sfsbno = XFS_B_TO_FSBT(mp, srcoff);
+       fsblen = XFS_B_TO_FSB(mp, len);
+       error = xfs_reflink_remap_blocks(src, sfsbno, dest, dfsbno, fsblen,
+                       destoff + len);
+       if (error)
+               goto out_error;
+
+       /*
+        * Carry the cowextsize hint from src to dest if we're sharing the
+        * entire source file to the entire destination file, the source file
+        * has a cowextsize hint, and the destination file does not.
+        */
+       cowextsize = 0;
+       if (srcoff == 0 && len == i_size_read(VFS_I(src)) &&
+           (src->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) &&
+           destoff == 0 && len >= i_size_read(VFS_I(dest)) &&
+           !(dest->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
+               cowextsize = src->i_d.di_cowextsize;
+
+       error = xfs_reflink_update_dest(dest, destoff + len, cowextsize);
+       if (error)
+               goto out_error;
+
+out_error:
+       xfs_iunlock(src, XFS_MMAPLOCK_EXCL);
+       xfs_iunlock(src, XFS_IOLOCK_EXCL);
+       if (src->i_ino != dest->i_ino) {
+               xfs_iunlock(dest, XFS_MMAPLOCK_EXCL);
+               xfs_iunlock(dest, XFS_IOLOCK_EXCL);
+       }
+       if (error)
+               trace_xfs_reflink_remap_range_error(dest, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * The user wants to preemptively CoW all shared blocks in this file,
+ * which enables us to turn off the reflink flag.  Iterate all
+ * extents which are not prealloc/delalloc to see which ranges are
+ * mentioned in the refcount tree, then read those blocks into the
+ * pagecache, dirty them, fsync them back out, and then we can update
+ * the inode flag.  What happens if we run out of memory? :)
+ */
+STATIC int
+xfs_reflink_dirty_extents(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           fbno,
+       xfs_filblks_t           end,
+       xfs_off_t               isize)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_agnumber_t          agno;
+       xfs_agblock_t           agbno;
+       xfs_extlen_t            aglen;
+       xfs_agblock_t           rbno;
+       xfs_extlen_t            rlen;
+       xfs_off_t               fpos;
+       xfs_off_t               flen;
+       struct xfs_bmbt_irec    map[2];
+       int                     nmaps;
+       int                     error = 0;
+
+       while (end - fbno > 0) {
+               nmaps = 1;
+               /*
+                * Look for extents in the file.  Skip holes, delalloc, or
+                * unwritten extents; they can't be reflinked.
+                */
+               error = xfs_bmapi_read(ip, fbno, end - fbno, map, &nmaps, 0);
+               if (error)
+                       goto out;
+               if (nmaps == 0)
+                       break;
+               if (map[0].br_startblock == HOLESTARTBLOCK ||
+                   map[0].br_startblock == DELAYSTARTBLOCK ||
+                   ISUNWRITTEN(&map[0]))
+                       goto next;
+
+               map[1] = map[0];
+               while (map[1].br_blockcount) {
+                       agno = XFS_FSB_TO_AGNO(mp, map[1].br_startblock);
+                       agbno = XFS_FSB_TO_AGBNO(mp, map[1].br_startblock);
+                       aglen = map[1].br_blockcount;
+
+                       error = xfs_reflink_find_shared(mp, agno, agbno, aglen,
+                                       &rbno, &rlen, true);
+                       if (error)
+                               goto out;
+                       if (rbno == NULLAGBLOCK)
+                               break;
+
+                       /* Dirty the pages */
+                       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+                       fpos = XFS_FSB_TO_B(mp, map[1].br_startoff +
+                                       (rbno - agbno));
+                       flen = XFS_FSB_TO_B(mp, rlen);
+                       if (fpos + flen > isize)
+                               flen = isize - fpos;
+                       error = iomap_file_dirty(VFS_I(ip), fpos, flen,
+                                       &xfs_iomap_ops);
+                       xfs_ilock(ip, XFS_ILOCK_EXCL);
+                       if (error)
+                               goto out;
+
+                       map[1].br_blockcount -= (rbno - agbno + rlen);
+                       map[1].br_startoff += (rbno - agbno + rlen);
+                       map[1].br_startblock += (rbno - agbno + rlen);
+               }
+
+next:
+               fbno = map[0].br_startoff + map[0].br_blockcount;
+       }
+out:
+       return error;
+}
+
+/* Clear the inode reflink flag if there are no shared extents. */
+int
+xfs_reflink_clear_inode_flag(
+       struct xfs_inode        *ip,
+       struct xfs_trans        **tpp)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_fileoff_t           fbno;
+       xfs_filblks_t           end;
+       xfs_agnumber_t          agno;
+       xfs_agblock_t           agbno;
+       xfs_extlen_t            aglen;
+       xfs_agblock_t           rbno;
+       xfs_extlen_t            rlen;
+       struct xfs_bmbt_irec    map;
+       int                     nmaps;
+       int                     error = 0;
+
+       ASSERT(xfs_is_reflink_inode(ip));
+
+       fbno = 0;
+       end = XFS_B_TO_FSB(mp, i_size_read(VFS_I(ip)));
+       while (end - fbno > 0) {
+               nmaps = 1;
+               /*
+                * Look for extents in the file.  Skip holes, delalloc, or
+                * unwritten extents; they can't be reflinked.
+                */
+               error = xfs_bmapi_read(ip, fbno, end - fbno, &map, &nmaps, 0);
+               if (error)
+                       return error;
+               if (nmaps == 0)
+                       break;
+               if (map.br_startblock == HOLESTARTBLOCK ||
+                   map.br_startblock == DELAYSTARTBLOCK ||
+                   ISUNWRITTEN(&map))
+                       goto next;
+
+               agno = XFS_FSB_TO_AGNO(mp, map.br_startblock);
+               agbno = XFS_FSB_TO_AGBNO(mp, map.br_startblock);
+               aglen = map.br_blockcount;
+
+               error = xfs_reflink_find_shared(mp, agno, agbno, aglen,
+                               &rbno, &rlen, false);
+               if (error)
+                       return error;
+               /* Is there still a shared block here? */
+               if (rbno != NULLAGBLOCK)
+                       return 0;
+next:
+               fbno = map.br_startoff + map.br_blockcount;
+       }
+
+       /*
+        * We didn't find any shared blocks so turn off the reflink flag.
+        * First, get rid of any leftover CoW mappings.
+        */
+       error = xfs_reflink_cancel_cow_blocks(ip, tpp, 0, NULLFILEOFF);
+       if (error)
+               return error;
+
+       /* Clear the inode flag. */
+       trace_xfs_reflink_unset_inode_flag(ip);
+       ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+       xfs_inode_clear_cowblocks_tag(ip);
+       xfs_trans_ijoin(*tpp, ip, 0);
+       xfs_trans_log_inode(*tpp, ip, XFS_ILOG_CORE);
+
+       return error;
+}
+
+/*
+ * Clear the inode reflink flag if there are no shared extents and the size
+ * hasn't changed.
+ */
+STATIC int
+xfs_reflink_try_clear_inode_flag(
+       struct xfs_inode        *ip)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       int                     error = 0;
+
+       /* Start a rolling transaction to remove the mappings */
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, 0, &tp);
+       if (error)
+               return error;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
+       error = xfs_reflink_clear_inode_flag(ip, &tp);
+       if (error)
+               goto cancel;
+
+       error = xfs_trans_commit(tp);
+       if (error)
+               goto out;
+
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return 0;
+cancel:
+       xfs_trans_cancel(tp);
+out:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return error;
+}
+
+/*
+ * Pre-COW all shared blocks within a given byte range of a file and turn off
+ * the reflink flag if we unshare all of the file's blocks.
+ */
+int
+xfs_reflink_unshare(
+       struct xfs_inode        *ip,
+       xfs_off_t               offset,
+       xfs_off_t               len)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_fileoff_t           fbno;
+       xfs_filblks_t           end;
+       xfs_off_t               isize;
+       int                     error;
+
+       if (!xfs_is_reflink_inode(ip))
+               return 0;
+
+       trace_xfs_reflink_unshare(ip, offset, len);
+
+       inode_dio_wait(VFS_I(ip));
+
+       /* Try to CoW the selected ranges */
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       fbno = XFS_B_TO_FSBT(mp, offset);
+       isize = i_size_read(VFS_I(ip));
+       end = XFS_B_TO_FSB(mp, offset + len);
+       error = xfs_reflink_dirty_extents(ip, fbno, end, isize);
+       if (error)
+               goto out_unlock;
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+       /* Wait for the IO to finish */
+       error = filemap_write_and_wait(VFS_I(ip)->i_mapping);
+       if (error)
+               goto out;
+
+       /* Turn off the reflink flag if possible. */
+       error = xfs_reflink_try_clear_inode_flag(ip);
+       if (error)
+               goto out;
+
+       return 0;
+
+out_unlock:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out:
+       trace_xfs_reflink_unshare_error(ip, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Does this inode have any real CoW reservations?
+ */
+bool
+xfs_reflink_has_real_cow_blocks(
+       struct xfs_inode                *ip)
+{
+       struct xfs_bmbt_irec            irec;
+       struct xfs_ifork                *ifp;
+       struct xfs_bmbt_rec_host        *gotp;
+       xfs_extnum_t                    idx;
+
+       if (!xfs_is_reflink_inode(ip))
+               return false;
+
+       /* Go find the old extent in the CoW fork. */
+       ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+       gotp = xfs_iext_bno_to_ext(ifp, 0, &idx);
+       while (gotp) {
+               xfs_bmbt_get_all(gotp, &irec);
+
+               if (!isnullstartblock(irec.br_startblock))
+                       return true;
+
+               /* Roll on... */
+               idx++;
+               if (idx >= ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
+                       break;
+               gotp = xfs_iext_get_ext(ifp, idx);
+       }
+
+       return false;
+}
diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h
new file mode 100644 (file)
index 0000000..5dc3c8a
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_REFLINK_H
+#define __XFS_REFLINK_H 1
+
+extern int xfs_reflink_find_shared(struct xfs_mount *mp, xfs_agnumber_t agno,
+               xfs_agblock_t agbno, xfs_extlen_t aglen, xfs_agblock_t *fbno,
+               xfs_extlen_t *flen, bool find_maximal);
+extern int xfs_reflink_trim_around_shared(struct xfs_inode *ip,
+               struct xfs_bmbt_irec *irec, bool *shared, bool *trimmed);
+
+extern int xfs_reflink_reserve_cow_range(struct xfs_inode *ip,
+               xfs_off_t offset, xfs_off_t count);
+extern int xfs_reflink_allocate_cow_range(struct xfs_inode *ip,
+               xfs_off_t offset, xfs_off_t count);
+extern bool xfs_reflink_find_cow_mapping(struct xfs_inode *ip, xfs_off_t offset,
+               struct xfs_bmbt_irec *imap, bool *need_alloc);
+extern int xfs_reflink_trim_irec_to_next_cow(struct xfs_inode *ip,
+               xfs_fileoff_t offset_fsb, struct xfs_bmbt_irec *imap);
+
+extern int xfs_reflink_cancel_cow_blocks(struct xfs_inode *ip,
+               struct xfs_trans **tpp, xfs_fileoff_t offset_fsb,
+               xfs_fileoff_t end_fsb);
+extern int xfs_reflink_cancel_cow_range(struct xfs_inode *ip, xfs_off_t offset,
+               xfs_off_t count);
+extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset,
+               xfs_off_t count);
+extern int xfs_reflink_recover_cow(struct xfs_mount *mp);
+#define XFS_REFLINK_DEDUPE     1       /* only reflink if contents match */
+#define XFS_REFLINK_ALL                (XFS_REFLINK_DEDUPE)
+extern int xfs_reflink_remap_range(struct xfs_inode *src, xfs_off_t srcoff,
+               struct xfs_inode *dest, xfs_off_t destoff, xfs_off_t len,
+               unsigned int flags);
+extern int xfs_reflink_clear_inode_flag(struct xfs_inode *ip,
+               struct xfs_trans **tpp);
+extern int xfs_reflink_unshare(struct xfs_inode *ip, xfs_off_t offset,
+               xfs_off_t len);
+
+extern bool xfs_reflink_has_real_cow_blocks(struct xfs_inode *ip);
+
+#endif /* __XFS_REFLINK_H */
index 0432a459871c6e086db5928bddfc4460d14562e6..73c827831551942550c3e0a3a66c4a7abdf52ef6 100644 (file)
@@ -441,8 +441,11 @@ xfs_rui_recover(
                                   XFS_FSB_TO_DADDR(mp, rmap->me_startblock));
                switch (rmap->me_flags & XFS_RMAP_EXTENT_TYPE_MASK) {
                case XFS_RMAP_EXTENT_MAP:
+               case XFS_RMAP_EXTENT_MAP_SHARED:
                case XFS_RMAP_EXTENT_UNMAP:
+               case XFS_RMAP_EXTENT_UNMAP_SHARED:
                case XFS_RMAP_EXTENT_CONVERT:
+               case XFS_RMAP_EXTENT_CONVERT_SHARED:
                case XFS_RMAP_EXTENT_ALLOC:
                case XFS_RMAP_EXTENT_FREE:
                        op_ok = true;
@@ -481,12 +484,21 @@ xfs_rui_recover(
                case XFS_RMAP_EXTENT_MAP:
                        type = XFS_RMAP_MAP;
                        break;
+               case XFS_RMAP_EXTENT_MAP_SHARED:
+                       type = XFS_RMAP_MAP_SHARED;
+                       break;
                case XFS_RMAP_EXTENT_UNMAP:
                        type = XFS_RMAP_UNMAP;
                        break;
+               case XFS_RMAP_EXTENT_UNMAP_SHARED:
+                       type = XFS_RMAP_UNMAP_SHARED;
+                       break;
                case XFS_RMAP_EXTENT_CONVERT:
                        type = XFS_RMAP_CONVERT;
                        break;
+               case XFS_RMAP_EXTENT_CONVERT_SHARED:
+                       type = XFS_RMAP_CONVERT_SHARED;
+                       break;
                case XFS_RMAP_EXTENT_ALLOC:
                        type = XFS_RMAP_ALLOC;
                        break;
index 6e812fe0fd43cc04b4f879c053c296f6cc1a5092..12d48cd8f8a423d74be6dad63a0d525042b344d4 100644 (file)
@@ -62,6 +62,7 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
                { "ibt2",               XFSSTAT_END_IBT_V2              },
                { "fibt2",              XFSSTAT_END_FIBT_V2             },
                { "rmapbt",             XFSSTAT_END_RMAP_V2             },
+               { "refcntbt",           XFSSTAT_END_REFCOUNT            },
                /* we print both series of quota information together */
                { "qm",                 XFSSTAT_END_QM                  },
        };
index 657865f51e78332dae8ff7892e93213c07f71e6e..79ad2e69fc33b61927ccfadf4d310575b42225b1 100644 (file)
@@ -213,7 +213,23 @@ struct xfsstats {
        __uint32_t              xs_rmap_2_alloc;
        __uint32_t              xs_rmap_2_free;
        __uint32_t              xs_rmap_2_moves;
-#define XFSSTAT_END_XQMSTAT            (XFSSTAT_END_RMAP_V2+6)
+#define XFSSTAT_END_REFCOUNT           (XFSSTAT_END_RMAP_V2 + 15)
+       __uint32_t              xs_refcbt_2_lookup;
+       __uint32_t              xs_refcbt_2_compare;
+       __uint32_t              xs_refcbt_2_insrec;
+       __uint32_t              xs_refcbt_2_delrec;
+       __uint32_t              xs_refcbt_2_newroot;
+       __uint32_t              xs_refcbt_2_killroot;
+       __uint32_t              xs_refcbt_2_increment;
+       __uint32_t              xs_refcbt_2_decrement;
+       __uint32_t              xs_refcbt_2_lshift;
+       __uint32_t              xs_refcbt_2_rshift;
+       __uint32_t              xs_refcbt_2_split;
+       __uint32_t              xs_refcbt_2_join;
+       __uint32_t              xs_refcbt_2_alloc;
+       __uint32_t              xs_refcbt_2_free;
+       __uint32_t              xs_refcbt_2_moves;
+#define XFSSTAT_END_XQMSTAT            (XFSSTAT_END_REFCOUNT + 6)
        __uint32_t              xs_qm_dqreclaims;
        __uint32_t              xs_qm_dqreclaim_misses;
        __uint32_t              xs_qm_dquot_dups;
index 2d092f9577ca2b41e4fb2f1713ddd17b67a67302..ade4691e3f7403ca65241856b355d0cb0f5709a0 100644 (file)
@@ -47,6 +47,9 @@
 #include "xfs_sysfs.h"
 #include "xfs_ondisk.h"
 #include "xfs_rmap_item.h"
+#include "xfs_refcount_item.h"
+#include "xfs_bmap_item.h"
+#include "xfs_reflink.h"
 
 #include <linux/namei.h>
 #include <linux/init.h>
@@ -936,6 +939,7 @@ xfs_fs_destroy_inode(
        struct inode            *inode)
 {
        struct xfs_inode        *ip = XFS_I(inode);
+       int                     error;
 
        trace_xfs_destroy_inode(ip);
 
@@ -943,6 +947,14 @@ xfs_fs_destroy_inode(
        XFS_STATS_INC(ip->i_mount, vn_rele);
        XFS_STATS_INC(ip->i_mount, vn_remove);
 
+       if (xfs_is_reflink_inode(ip)) {
+               error = xfs_reflink_cancel_cow_range(ip, 0, NULLFILEOFF);
+               if (error && !XFS_FORCED_SHUTDOWN(ip->i_mount))
+                       xfs_warn(ip->i_mount,
+"Error %d while evicting CoW blocks for inode %llu.",
+                                       error, ip->i_ino);
+       }
+
        xfs_inactive(ip);
 
        ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) || ip->i_delayed_blks == 0);
@@ -1006,6 +1018,16 @@ xfs_fs_drop_inode(
 {
        struct xfs_inode        *ip = XFS_I(inode);
 
+       /*
+        * If this unlinked inode is in the middle of recovery, don't
+        * drop the inode just yet; log recovery will take care of
+        * that.  See the comment for this inode flag.
+        */
+       if (ip->i_flags & XFS_IRECOVERY) {
+               ASSERT(ip->i_mount->m_log->l_flags & XLOG_RECOVERY_NEEDED);
+               return 0;
+       }
+
        return generic_drop_inode(inode) || (ip->i_flags & XFS_IDONTCACHE);
 }
 
@@ -1296,10 +1318,31 @@ xfs_fs_remount(
                xfs_restore_resvblks(mp);
                xfs_log_work_queue(mp);
                xfs_queue_eofblocks(mp);
+
+               /* Recover any CoW blocks that never got remapped. */
+               error = xfs_reflink_recover_cow(mp);
+               if (error) {
+                       xfs_err(mp,
+       "Error %d recovering leftover CoW allocations.", error);
+                       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+                       return error;
+               }
+
+               /* Create the per-AG metadata reservation pool .*/
+               error = xfs_fs_reserve_ag_blocks(mp);
+               if (error && error != -ENOSPC)
+                       return error;
        }
 
        /* rw -> ro */
        if (!(mp->m_flags & XFS_MOUNT_RDONLY) && (*flags & MS_RDONLY)) {
+               /* Free the per-AG metadata reservation pool. */
+               error = xfs_fs_unreserve_ag_blocks(mp);
+               if (error) {
+                       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+                       return error;
+               }
+
                /*
                 * Before we sync the metadata, we need to free up the reserve
                 * block pool so that the used block count in the superblock on
@@ -1490,6 +1533,7 @@ xfs_fs_fill_super(
        atomic_set(&mp->m_active_trans, 0);
        INIT_DELAYED_WORK(&mp->m_reclaim_work, xfs_reclaim_worker);
        INIT_DELAYED_WORK(&mp->m_eofblocks_work, xfs_eofblocks_worker);
+       INIT_DELAYED_WORK(&mp->m_cowblocks_work, xfs_cowblocks_worker);
        mp->m_kobj.kobject.kset = xfs_kset;
 
        mp->m_super = sb;
@@ -1572,6 +1616,9 @@ xfs_fs_fill_super(
                        "DAX unsupported by block device. Turning off DAX.");
                        mp->m_flags &= ~XFS_MOUNT_DAX;
                }
+               if (xfs_sb_version_hasreflink(&mp->m_sb))
+                       xfs_alert(mp,
+               "DAX and reflink have not been tested together!");
        }
 
        if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
@@ -1585,6 +1632,10 @@ xfs_fs_fill_super(
        "EXPERIMENTAL reverse mapping btree feature enabled. Use at your own risk!");
        }
 
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               xfs_alert(mp,
+       "EXPERIMENTAL reflink feature enabled. Use at your own risk!");
+
        error = xfs_mountfs(mp);
        if (error)
                goto out_filestream_unmount;
@@ -1788,8 +1839,38 @@ xfs_init_zones(void)
        if (!xfs_rui_zone)
                goto out_destroy_rud_zone;
 
+       xfs_cud_zone = kmem_zone_init(sizeof(struct xfs_cud_log_item),
+                       "xfs_cud_item");
+       if (!xfs_cud_zone)
+               goto out_destroy_rui_zone;
+
+       xfs_cui_zone = kmem_zone_init(
+                       xfs_cui_log_item_sizeof(XFS_CUI_MAX_FAST_EXTENTS),
+                       "xfs_cui_item");
+       if (!xfs_cui_zone)
+               goto out_destroy_cud_zone;
+
+       xfs_bud_zone = kmem_zone_init(sizeof(struct xfs_bud_log_item),
+                       "xfs_bud_item");
+       if (!xfs_bud_zone)
+               goto out_destroy_cui_zone;
+
+       xfs_bui_zone = kmem_zone_init(
+                       xfs_bui_log_item_sizeof(XFS_BUI_MAX_FAST_EXTENTS),
+                       "xfs_bui_item");
+       if (!xfs_bui_zone)
+               goto out_destroy_bud_zone;
+
        return 0;
 
+ out_destroy_bud_zone:
+       kmem_zone_destroy(xfs_bud_zone);
+ out_destroy_cui_zone:
+       kmem_zone_destroy(xfs_cui_zone);
+ out_destroy_cud_zone:
+       kmem_zone_destroy(xfs_cud_zone);
+ out_destroy_rui_zone:
+       kmem_zone_destroy(xfs_rui_zone);
  out_destroy_rud_zone:
        kmem_zone_destroy(xfs_rud_zone);
  out_destroy_icreate_zone:
@@ -1832,6 +1913,10 @@ xfs_destroy_zones(void)
         * destroy caches.
         */
        rcu_barrier();
+       kmem_zone_destroy(xfs_bui_zone);
+       kmem_zone_destroy(xfs_bud_zone);
+       kmem_zone_destroy(xfs_cui_zone);
+       kmem_zone_destroy(xfs_cud_zone);
        kmem_zone_destroy(xfs_rui_zone);
        kmem_zone_destroy(xfs_rud_zone);
        kmem_zone_destroy(xfs_icreate_zone);
@@ -1885,6 +1970,8 @@ init_xfs_fs(void)
 
        xfs_extent_free_init_defer_op();
        xfs_rmap_update_init_defer_op();
+       xfs_refcount_update_init_defer_op();
+       xfs_bmap_update_init_defer_op();
 
        xfs_dir_startup();
 
index aed74d3f8da93a9b80b7bafe8b76f004b481ca5a..afe1f66aaa6980a682ae6a736351115a5ae87ac1 100644 (file)
@@ -184,6 +184,15 @@ static struct ctl_table xfs_table[] = {
                .extra1         = &xfs_params.eofb_timer.min,
                .extra2         = &xfs_params.eofb_timer.max,
        },
+       {
+               .procname       = "speculative_cow_prealloc_lifetime",
+               .data           = &xfs_params.cowb_timer.val,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &xfs_params.cowb_timer.min,
+               .extra2         = &xfs_params.cowb_timer.max,
+       },
        /* please keep this the last entry */
 #ifdef CONFIG_PROC_FS
        {
index ffef453757543a94d742eb2890e4858e8bcd7716..984a3499cfe3870c4e8f0c02e3c3c48a2b18fad9 100644 (file)
@@ -48,6 +48,7 @@ typedef struct xfs_param {
        xfs_sysctl_val_t inherit_nodfrg;/* Inherit the "nodefrag" inode flag. */
        xfs_sysctl_val_t fstrm_timer;   /* Filestream dir-AG assoc'n timeout. */
        xfs_sysctl_val_t eofb_timer;    /* Interval between eofb scan wakeups */
+       xfs_sysctl_val_t cowb_timer;    /* Interval between cowb scan wakeups */
 } xfs_param_t;
 
 /*
index 16093c7dacded42886e9dbbb451f902f65c5ff62..ad188d3a83f3739db19ed8af577fe4259c9267e8 100644 (file)
@@ -39,6 +39,7 @@ struct xfs_buf_log_format;
 struct xfs_inode_log_format;
 struct xfs_bmbt_irec;
 struct xfs_btree_cur;
+struct xfs_refcount_irec;
 
 DECLARE_EVENT_CLASS(xfs_attr_list_class,
        TP_PROTO(struct xfs_attr_list_context *ctx),
@@ -135,6 +136,8 @@ DEFINE_PERAG_REF_EVENT(xfs_perag_set_reclaim);
 DEFINE_PERAG_REF_EVENT(xfs_perag_clear_reclaim);
 DEFINE_PERAG_REF_EVENT(xfs_perag_set_eofblocks);
 DEFINE_PERAG_REF_EVENT(xfs_perag_clear_eofblocks);
+DEFINE_PERAG_REF_EVENT(xfs_perag_set_cowblocks);
+DEFINE_PERAG_REF_EVENT(xfs_perag_clear_cowblocks);
 
 DECLARE_EVENT_CLASS(xfs_ag_class,
        TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno),
@@ -268,10 +271,10 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
                __field(unsigned long, caller_ip)
        ),
        TP_fast_assign(
-               struct xfs_ifork        *ifp = (state & BMAP_ATTRFORK) ?
-                                               ip->i_afp : &ip->i_df;
+               struct xfs_ifork        *ifp;
                struct xfs_bmbt_irec    r;
 
+               ifp = xfs_iext_state_to_fork(ip, state);
                xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), &r);
                __entry->dev = VFS_I(ip)->i_sb->s_dev;
                __entry->ino = ip->i_ino;
@@ -686,6 +689,9 @@ DEFINE_INODE_EVENT(xfs_dquot_dqdetach);
 DEFINE_INODE_EVENT(xfs_inode_set_eofblocks_tag);
 DEFINE_INODE_EVENT(xfs_inode_clear_eofblocks_tag);
 DEFINE_INODE_EVENT(xfs_inode_free_eofblocks_invalid);
+DEFINE_INODE_EVENT(xfs_inode_set_cowblocks_tag);
+DEFINE_INODE_EVENT(xfs_inode_clear_cowblocks_tag);
+DEFINE_INODE_EVENT(xfs_inode_free_cowblocks_invalid);
 
 DEFINE_INODE_EVENT(xfs_filemap_fault);
 DEFINE_INODE_EVENT(xfs_filemap_pmd_fault);
@@ -2581,10 +2587,20 @@ DEFINE_RMAPBT_EVENT(xfs_rmap_delete);
 DEFINE_AG_ERROR_EVENT(xfs_rmap_insert_error);
 DEFINE_AG_ERROR_EVENT(xfs_rmap_delete_error);
 DEFINE_AG_ERROR_EVENT(xfs_rmap_update_error);
+
+DEFINE_RMAPBT_EVENT(xfs_rmap_find_left_neighbor_candidate);
+DEFINE_RMAPBT_EVENT(xfs_rmap_find_left_neighbor_query);
+DEFINE_RMAPBT_EVENT(xfs_rmap_lookup_le_range_candidate);
+DEFINE_RMAPBT_EVENT(xfs_rmap_lookup_le_range);
 DEFINE_RMAPBT_EVENT(xfs_rmap_lookup_le_range_result);
 DEFINE_RMAPBT_EVENT(xfs_rmap_find_right_neighbor_result);
 DEFINE_RMAPBT_EVENT(xfs_rmap_find_left_neighbor_result);
 
+/* deferred bmbt updates */
+#define DEFINE_BMAP_DEFERRED_EVENT     DEFINE_RMAP_DEFERRED_EVENT
+DEFINE_BMAP_DEFERRED_EVENT(xfs_bmap_defer);
+DEFINE_BMAP_DEFERRED_EVENT(xfs_bmap_deferred);
+
 /* per-AG reservation */
 DECLARE_EVENT_CLASS(xfs_ag_resv_class,
        TP_PROTO(struct xfs_perag *pag, enum xfs_ag_resv_type resv,
@@ -2639,6 +2655,728 @@ DEFINE_AG_RESV_EVENT(xfs_ag_resv_needed);
 DEFINE_AG_ERROR_EVENT(xfs_ag_resv_free_error);
 DEFINE_AG_ERROR_EVENT(xfs_ag_resv_init_error);
 
+/* refcount tracepoint classes */
+
+/* reuse the discard trace class for agbno/aglen-based traces */
+#define DEFINE_AG_EXTENT_EVENT(name) DEFINE_DISCARD_EVENT(name)
+
+/* ag btree lookup tracepoint class */
+#define XFS_AG_BTREE_CMP_FORMAT_STR \
+       { XFS_LOOKUP_EQ,        "eq" }, \
+       { XFS_LOOKUP_LE,        "le" }, \
+       { XFS_LOOKUP_GE,        "ge" }
+DECLARE_EVENT_CLASS(xfs_ag_btree_lookup_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                xfs_agblock_t agbno, xfs_lookup_t dir),
+       TP_ARGS(mp, agno, agbno, dir),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, agbno)
+               __field(xfs_lookup_t, dir)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->agbno = agbno;
+               __entry->dir = dir;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u cmp %s(%d)\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->agbno,
+                 __print_symbolic(__entry->dir, XFS_AG_BTREE_CMP_FORMAT_STR),
+                 __entry->dir)
+)
+
+#define DEFINE_AG_BTREE_LOOKUP_EVENT(name) \
+DEFINE_EVENT(xfs_ag_btree_lookup_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                xfs_agblock_t agbno, xfs_lookup_t dir), \
+       TP_ARGS(mp, agno, agbno, dir))
+
+/* single-rcext tracepoint class */
+DECLARE_EVENT_CLASS(xfs_refcount_extent_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                struct xfs_refcount_irec *irec),
+       TP_ARGS(mp, agno, irec),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, startblock)
+               __field(xfs_extlen_t, blockcount)
+               __field(xfs_nlink_t, refcount)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->startblock = irec->rc_startblock;
+               __entry->blockcount = irec->rc_blockcount;
+               __entry->refcount = irec->rc_refcount;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->startblock,
+                 __entry->blockcount,
+                 __entry->refcount)
+)
+
+#define DEFINE_REFCOUNT_EXTENT_EVENT(name) \
+DEFINE_EVENT(xfs_refcount_extent_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                struct xfs_refcount_irec *irec), \
+       TP_ARGS(mp, agno, irec))
+
+/* single-rcext and an agbno tracepoint class */
+DECLARE_EVENT_CLASS(xfs_refcount_extent_at_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                struct xfs_refcount_irec *irec, xfs_agblock_t agbno),
+       TP_ARGS(mp, agno, irec, agbno),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, startblock)
+               __field(xfs_extlen_t, blockcount)
+               __field(xfs_nlink_t, refcount)
+               __field(xfs_agblock_t, agbno)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->startblock = irec->rc_startblock;
+               __entry->blockcount = irec->rc_blockcount;
+               __entry->refcount = irec->rc_refcount;
+               __entry->agbno = agbno;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u @ agbno %u\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->startblock,
+                 __entry->blockcount,
+                 __entry->refcount,
+                 __entry->agbno)
+)
+
+#define DEFINE_REFCOUNT_EXTENT_AT_EVENT(name) \
+DEFINE_EVENT(xfs_refcount_extent_at_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                struct xfs_refcount_irec *irec, xfs_agblock_t agbno), \
+       TP_ARGS(mp, agno, irec, agbno))
+
+/* double-rcext tracepoint class */
+DECLARE_EVENT_CLASS(xfs_refcount_double_extent_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2),
+       TP_ARGS(mp, agno, i1, i2),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, i1_startblock)
+               __field(xfs_extlen_t, i1_blockcount)
+               __field(xfs_nlink_t, i1_refcount)
+               __field(xfs_agblock_t, i2_startblock)
+               __field(xfs_extlen_t, i2_blockcount)
+               __field(xfs_nlink_t, i2_refcount)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->i1_startblock = i1->rc_startblock;
+               __entry->i1_blockcount = i1->rc_blockcount;
+               __entry->i1_refcount = i1->rc_refcount;
+               __entry->i2_startblock = i2->rc_startblock;
+               __entry->i2_blockcount = i2->rc_blockcount;
+               __entry->i2_refcount = i2->rc_refcount;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u -- "
+                 "agbno %u len %u refcount %u\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->i1_startblock,
+                 __entry->i1_blockcount,
+                 __entry->i1_refcount,
+                 __entry->i2_startblock,
+                 __entry->i2_blockcount,
+                 __entry->i2_refcount)
+)
+
+#define DEFINE_REFCOUNT_DOUBLE_EXTENT_EVENT(name) \
+DEFINE_EVENT(xfs_refcount_double_extent_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2), \
+       TP_ARGS(mp, agno, i1, i2))
+
+/* double-rcext and an agbno tracepoint class */
+DECLARE_EVENT_CLASS(xfs_refcount_double_extent_at_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2,
+                xfs_agblock_t agbno),
+       TP_ARGS(mp, agno, i1, i2, agbno),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, i1_startblock)
+               __field(xfs_extlen_t, i1_blockcount)
+               __field(xfs_nlink_t, i1_refcount)
+               __field(xfs_agblock_t, i2_startblock)
+               __field(xfs_extlen_t, i2_blockcount)
+               __field(xfs_nlink_t, i2_refcount)
+               __field(xfs_agblock_t, agbno)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->i1_startblock = i1->rc_startblock;
+               __entry->i1_blockcount = i1->rc_blockcount;
+               __entry->i1_refcount = i1->rc_refcount;
+               __entry->i2_startblock = i2->rc_startblock;
+               __entry->i2_blockcount = i2->rc_blockcount;
+               __entry->i2_refcount = i2->rc_refcount;
+               __entry->agbno = agbno;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u -- "
+                 "agbno %u len %u refcount %u @ agbno %u\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->i1_startblock,
+                 __entry->i1_blockcount,
+                 __entry->i1_refcount,
+                 __entry->i2_startblock,
+                 __entry->i2_blockcount,
+                 __entry->i2_refcount,
+                 __entry->agbno)
+)
+
+#define DEFINE_REFCOUNT_DOUBLE_EXTENT_AT_EVENT(name) \
+DEFINE_EVENT(xfs_refcount_double_extent_at_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2, \
+                xfs_agblock_t agbno), \
+       TP_ARGS(mp, agno, i1, i2, agbno))
+
+/* triple-rcext tracepoint class */
+DECLARE_EVENT_CLASS(xfs_refcount_triple_extent_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2,
+                struct xfs_refcount_irec *i3),
+       TP_ARGS(mp, agno, i1, i2, i3),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, i1_startblock)
+               __field(xfs_extlen_t, i1_blockcount)
+               __field(xfs_nlink_t, i1_refcount)
+               __field(xfs_agblock_t, i2_startblock)
+               __field(xfs_extlen_t, i2_blockcount)
+               __field(xfs_nlink_t, i2_refcount)
+               __field(xfs_agblock_t, i3_startblock)
+               __field(xfs_extlen_t, i3_blockcount)
+               __field(xfs_nlink_t, i3_refcount)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->i1_startblock = i1->rc_startblock;
+               __entry->i1_blockcount = i1->rc_blockcount;
+               __entry->i1_refcount = i1->rc_refcount;
+               __entry->i2_startblock = i2->rc_startblock;
+               __entry->i2_blockcount = i2->rc_blockcount;
+               __entry->i2_refcount = i2->rc_refcount;
+               __entry->i3_startblock = i3->rc_startblock;
+               __entry->i3_blockcount = i3->rc_blockcount;
+               __entry->i3_refcount = i3->rc_refcount;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u -- "
+                 "agbno %u len %u refcount %u -- "
+                 "agbno %u len %u refcount %u\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->i1_startblock,
+                 __entry->i1_blockcount,
+                 __entry->i1_refcount,
+                 __entry->i2_startblock,
+                 __entry->i2_blockcount,
+                 __entry->i2_refcount,
+                 __entry->i3_startblock,
+                 __entry->i3_blockcount,
+                 __entry->i3_refcount)
+);
+
+#define DEFINE_REFCOUNT_TRIPLE_EXTENT_EVENT(name) \
+DEFINE_EVENT(xfs_refcount_triple_extent_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2, \
+                struct xfs_refcount_irec *i3), \
+       TP_ARGS(mp, agno, i1, i2, i3))
+
+/* refcount btree tracepoints */
+DEFINE_BUSY_EVENT(xfs_refcountbt_alloc_block);
+DEFINE_BUSY_EVENT(xfs_refcountbt_free_block);
+DEFINE_AG_BTREE_LOOKUP_EVENT(xfs_refcount_lookup);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_get);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_update);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_insert);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_delete);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_insert_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_delete_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_update_error);
+
+/* refcount adjustment tracepoints */
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_increase);
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_decrease);
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_cow_increase);
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_cow_decrease);
+DEFINE_REFCOUNT_TRIPLE_EXTENT_EVENT(xfs_refcount_merge_center_extents);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_modify_extent);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_recover_extent);
+DEFINE_REFCOUNT_EXTENT_AT_EVENT(xfs_refcount_split_extent);
+DEFINE_REFCOUNT_DOUBLE_EXTENT_EVENT(xfs_refcount_merge_left_extent);
+DEFINE_REFCOUNT_DOUBLE_EXTENT_EVENT(xfs_refcount_merge_right_extent);
+DEFINE_REFCOUNT_DOUBLE_EXTENT_AT_EVENT(xfs_refcount_find_left_extent);
+DEFINE_REFCOUNT_DOUBLE_EXTENT_AT_EVENT(xfs_refcount_find_right_extent);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_adjust_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_adjust_cow_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_merge_center_extents_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_modify_extent_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_split_extent_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_merge_left_extent_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_merge_right_extent_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_find_left_extent_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_find_right_extent_error);
+
+/* reflink helpers */
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_find_shared);
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_find_shared_result);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_find_shared_error);
+#define DEFINE_REFCOUNT_DEFERRED_EVENT DEFINE_PHYS_EXTENT_DEFERRED_EVENT
+DEFINE_REFCOUNT_DEFERRED_EVENT(xfs_refcount_defer);
+DEFINE_REFCOUNT_DEFERRED_EVENT(xfs_refcount_deferred);
+
+TRACE_EVENT(xfs_refcount_finish_one_leftover,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                int type, xfs_agblock_t agbno, xfs_extlen_t len,
+                xfs_agblock_t new_agbno, xfs_extlen_t new_len),
+       TP_ARGS(mp, agno, type, agbno, len, new_agbno, new_len),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(int, type)
+               __field(xfs_agblock_t, agbno)
+               __field(xfs_extlen_t, len)
+               __field(xfs_agblock_t, new_agbno)
+               __field(xfs_extlen_t, new_len)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->type = type;
+               __entry->agbno = agbno;
+               __entry->len = len;
+               __entry->new_agbno = new_agbno;
+               __entry->new_len = new_len;
+       ),
+       TP_printk("dev %d:%d type %d agno %u agbno %u len %u new_agbno %u new_len %u",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->type,
+                 __entry->agno,
+                 __entry->agbno,
+                 __entry->len,
+                 __entry->new_agbno,
+                 __entry->new_len)
+);
+
+/* simple inode-based error/%ip tracepoint class */
+DECLARE_EVENT_CLASS(xfs_inode_error_class,
+       TP_PROTO(struct xfs_inode *ip, int error, unsigned long caller_ip),
+       TP_ARGS(ip, error, caller_ip),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(int, error)
+               __field(unsigned long, caller_ip)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->error = error;
+               __entry->caller_ip = caller_ip;
+       ),
+       TP_printk("dev %d:%d ino %llx error %d caller %ps",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->error,
+                 (char *)__entry->caller_ip)
+);
+
+#define DEFINE_INODE_ERROR_EVENT(name) \
+DEFINE_EVENT(xfs_inode_error_class, name, \
+       TP_PROTO(struct xfs_inode *ip, int error, \
+                unsigned long caller_ip), \
+       TP_ARGS(ip, error, caller_ip))
+
+/* reflink allocator */
+TRACE_EVENT(xfs_bmap_remap_alloc,
+       TP_PROTO(struct xfs_inode *ip, xfs_fsblock_t fsbno,
+                xfs_extlen_t len),
+       TP_ARGS(ip, fsbno, len),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(xfs_fsblock_t, fsbno)
+               __field(xfs_extlen_t, len)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->fsbno = fsbno;
+               __entry->len = len;
+       ),
+       TP_printk("dev %d:%d ino 0x%llx fsbno 0x%llx len %x",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->fsbno,
+                 __entry->len)
+);
+DEFINE_INODE_ERROR_EVENT(xfs_bmap_remap_alloc_error);
+
+/* reflink tracepoint classes */
+
+/* two-file io tracepoint class */
+DECLARE_EVENT_CLASS(xfs_double_io_class,
+       TP_PROTO(struct xfs_inode *src, xfs_off_t soffset, xfs_off_t len,
+                struct xfs_inode *dest, xfs_off_t doffset),
+       TP_ARGS(src, soffset, len, dest, doffset),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, src_ino)
+               __field(loff_t, src_isize)
+               __field(loff_t, src_disize)
+               __field(loff_t, src_offset)
+               __field(size_t, len)
+               __field(xfs_ino_t, dest_ino)
+               __field(loff_t, dest_isize)
+               __field(loff_t, dest_disize)
+               __field(loff_t, dest_offset)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(src)->i_sb->s_dev;
+               __entry->src_ino = src->i_ino;
+               __entry->src_isize = VFS_I(src)->i_size;
+               __entry->src_disize = src->i_d.di_size;
+               __entry->src_offset = soffset;
+               __entry->len = len;
+               __entry->dest_ino = dest->i_ino;
+               __entry->dest_isize = VFS_I(dest)->i_size;
+               __entry->dest_disize = dest->i_d.di_size;
+               __entry->dest_offset = doffset;
+       ),
+       TP_printk("dev %d:%d count %zd "
+                 "ino 0x%llx isize 0x%llx disize 0x%llx offset 0x%llx -> "
+                 "ino 0x%llx isize 0x%llx disize 0x%llx offset 0x%llx",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->len,
+                 __entry->src_ino,
+                 __entry->src_isize,
+                 __entry->src_disize,
+                 __entry->src_offset,
+                 __entry->dest_ino,
+                 __entry->dest_isize,
+                 __entry->dest_disize,
+                 __entry->dest_offset)
+)
+
+#define DEFINE_DOUBLE_IO_EVENT(name)   \
+DEFINE_EVENT(xfs_double_io_class, name,        \
+       TP_PROTO(struct xfs_inode *src, xfs_off_t soffset, xfs_off_t len, \
+                struct xfs_inode *dest, xfs_off_t doffset), \
+       TP_ARGS(src, soffset, len, dest, doffset))
+
+/* two-file vfs io tracepoint class */
+DECLARE_EVENT_CLASS(xfs_double_vfs_io_class,
+       TP_PROTO(struct inode *src, u64 soffset, u64 len,
+                struct inode *dest, u64 doffset),
+       TP_ARGS(src, soffset, len, dest, doffset),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(unsigned long, src_ino)
+               __field(loff_t, src_isize)
+               __field(loff_t, src_offset)
+               __field(size_t, len)
+               __field(unsigned long, dest_ino)
+               __field(loff_t, dest_isize)
+               __field(loff_t, dest_offset)
+       ),
+       TP_fast_assign(
+               __entry->dev = src->i_sb->s_dev;
+               __entry->src_ino = src->i_ino;
+               __entry->src_isize = i_size_read(src);
+               __entry->src_offset = soffset;
+               __entry->len = len;
+               __entry->dest_ino = dest->i_ino;
+               __entry->dest_isize = i_size_read(dest);
+               __entry->dest_offset = doffset;
+       ),
+       TP_printk("dev %d:%d count %zd "
+                 "ino 0x%lx isize 0x%llx offset 0x%llx -> "
+                 "ino 0x%lx isize 0x%llx offset 0x%llx",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->len,
+                 __entry->src_ino,
+                 __entry->src_isize,
+                 __entry->src_offset,
+                 __entry->dest_ino,
+                 __entry->dest_isize,
+                 __entry->dest_offset)
+)
+
+#define DEFINE_DOUBLE_VFS_IO_EVENT(name)       \
+DEFINE_EVENT(xfs_double_vfs_io_class, name,    \
+       TP_PROTO(struct inode *src, u64 soffset, u64 len, \
+                struct inode *dest, u64 doffset), \
+       TP_ARGS(src, soffset, len, dest, doffset))
+
+/* CoW write tracepoint */
+DECLARE_EVENT_CLASS(xfs_copy_on_write_class,
+       TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t lblk, xfs_fsblock_t pblk,
+                xfs_extlen_t len, xfs_fsblock_t new_pblk),
+       TP_ARGS(ip, lblk, pblk, len, new_pblk),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(xfs_fileoff_t, lblk)
+               __field(xfs_fsblock_t, pblk)
+               __field(xfs_extlen_t, len)
+               __field(xfs_fsblock_t, new_pblk)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->lblk = lblk;
+               __entry->pblk = pblk;
+               __entry->len = len;
+               __entry->new_pblk = new_pblk;
+       ),
+       TP_printk("dev %d:%d ino 0x%llx lblk 0x%llx pblk 0x%llx "
+                 "len 0x%x new_pblk %llu",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->lblk,
+                 __entry->pblk,
+                 __entry->len,
+                 __entry->new_pblk)
+)
+
+#define DEFINE_COW_EVENT(name) \
+DEFINE_EVENT(xfs_copy_on_write_class, name,    \
+       TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t lblk, xfs_fsblock_t pblk, \
+                xfs_extlen_t len, xfs_fsblock_t new_pblk), \
+       TP_ARGS(ip, lblk, pblk, len, new_pblk))
+
+/* inode/irec events */
+DECLARE_EVENT_CLASS(xfs_inode_irec_class,
+       TP_PROTO(struct xfs_inode *ip, struct xfs_bmbt_irec *irec),
+       TP_ARGS(ip, irec),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(xfs_fileoff_t, lblk)
+               __field(xfs_extlen_t, len)
+               __field(xfs_fsblock_t, pblk)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->lblk = irec->br_startoff;
+               __entry->len = irec->br_blockcount;
+               __entry->pblk = irec->br_startblock;
+       ),
+       TP_printk("dev %d:%d ino 0x%llx lblk 0x%llx len 0x%x pblk %llu",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->lblk,
+                 __entry->len,
+                 __entry->pblk)
+);
+#define DEFINE_INODE_IREC_EVENT(name) \
+DEFINE_EVENT(xfs_inode_irec_class, name, \
+       TP_PROTO(struct xfs_inode *ip, struct xfs_bmbt_irec *irec), \
+       TP_ARGS(ip, irec))
+
+/* refcount/reflink tracepoint definitions */
+
+/* reflink tracepoints */
+DEFINE_INODE_EVENT(xfs_reflink_set_inode_flag);
+DEFINE_INODE_EVENT(xfs_reflink_unset_inode_flag);
+DEFINE_ITRUNC_EVENT(xfs_reflink_update_inode_size);
+DEFINE_IOMAP_EVENT(xfs_reflink_remap_imap);
+TRACE_EVENT(xfs_reflink_remap_blocks_loop,
+       TP_PROTO(struct xfs_inode *src, xfs_fileoff_t soffset,
+                xfs_filblks_t len, struct xfs_inode *dest,
+                xfs_fileoff_t doffset),
+       TP_ARGS(src, soffset, len, dest, doffset),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, src_ino)
+               __field(xfs_fileoff_t, src_lblk)
+               __field(xfs_filblks_t, len)
+               __field(xfs_ino_t, dest_ino)
+               __field(xfs_fileoff_t, dest_lblk)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(src)->i_sb->s_dev;
+               __entry->src_ino = src->i_ino;
+               __entry->src_lblk = soffset;
+               __entry->len = len;
+               __entry->dest_ino = dest->i_ino;
+               __entry->dest_lblk = doffset;
+       ),
+       TP_printk("dev %d:%d len 0x%llx "
+                 "ino 0x%llx offset 0x%llx blocks -> "
+                 "ino 0x%llx offset 0x%llx blocks",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->len,
+                 __entry->src_ino,
+                 __entry->src_lblk,
+                 __entry->dest_ino,
+                 __entry->dest_lblk)
+);
+TRACE_EVENT(xfs_reflink_punch_range,
+       TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t lblk,
+                xfs_extlen_t len),
+       TP_ARGS(ip, lblk, len),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(xfs_fileoff_t, lblk)
+               __field(xfs_extlen_t, len)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->lblk = lblk;
+               __entry->len = len;
+       ),
+       TP_printk("dev %d:%d ino 0x%llx lblk 0x%llx len 0x%x",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->lblk,
+                 __entry->len)
+);
+TRACE_EVENT(xfs_reflink_remap,
+       TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t lblk,
+                xfs_extlen_t len, xfs_fsblock_t new_pblk),
+       TP_ARGS(ip, lblk, len, new_pblk),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(xfs_fileoff_t, lblk)
+               __field(xfs_extlen_t, len)
+               __field(xfs_fsblock_t, new_pblk)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->lblk = lblk;
+               __entry->len = len;
+               __entry->new_pblk = new_pblk;
+       ),
+       TP_printk("dev %d:%d ino 0x%llx lblk 0x%llx len 0x%x new_pblk %llu",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->lblk,
+                 __entry->len,
+                 __entry->new_pblk)
+);
+DEFINE_DOUBLE_IO_EVENT(xfs_reflink_remap_range);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_remap_range_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_set_inode_flag_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_update_inode_size_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_reflink_main_loop_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_read_iomap_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_remap_blocks_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_remap_extent_error);
+
+/* dedupe tracepoints */
+DEFINE_DOUBLE_IO_EVENT(xfs_reflink_compare_extents);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_compare_extents_error);
+
+/* ioctl tracepoints */
+DEFINE_DOUBLE_VFS_IO_EVENT(xfs_ioctl_reflink);
+DEFINE_DOUBLE_VFS_IO_EVENT(xfs_ioctl_clone_range);
+DEFINE_DOUBLE_VFS_IO_EVENT(xfs_ioctl_file_extent_same);
+TRACE_EVENT(xfs_ioctl_clone,
+       TP_PROTO(struct inode *src, struct inode *dest),
+       TP_ARGS(src, dest),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(unsigned long, src_ino)
+               __field(loff_t, src_isize)
+               __field(unsigned long, dest_ino)
+               __field(loff_t, dest_isize)
+       ),
+       TP_fast_assign(
+               __entry->dev = src->i_sb->s_dev;
+               __entry->src_ino = src->i_ino;
+               __entry->src_isize = i_size_read(src);
+               __entry->dest_ino = dest->i_ino;
+               __entry->dest_isize = i_size_read(dest);
+       ),
+       TP_printk("dev %d:%d "
+                 "ino 0x%lx isize 0x%llx -> "
+                 "ino 0x%lx isize 0x%llx\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->src_ino,
+                 __entry->src_isize,
+                 __entry->dest_ino,
+                 __entry->dest_isize)
+);
+
+/* unshare tracepoints */
+DEFINE_SIMPLE_IO_EVENT(xfs_reflink_unshare);
+DEFINE_SIMPLE_IO_EVENT(xfs_reflink_cow_eof_block);
+DEFINE_PAGE_EVENT(xfs_reflink_unshare_page);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_unshare_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_cow_eof_block_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_dirty_page_error);
+
+/* copy on write */
+DEFINE_INODE_IREC_EVENT(xfs_reflink_trim_around_shared);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_alloc);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_found);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_enospc);
+
+DEFINE_RW_EVENT(xfs_reflink_reserve_cow_range);
+DEFINE_RW_EVENT(xfs_reflink_allocate_cow_range);
+
+DEFINE_INODE_IREC_EVENT(xfs_reflink_bounce_dio_write);
+DEFINE_IOMAP_EVENT(xfs_reflink_find_cow_mapping);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_trim_irec);
+
+DEFINE_SIMPLE_IO_EVENT(xfs_reflink_cancel_cow_range);
+DEFINE_SIMPLE_IO_EVENT(xfs_reflink_end_cow);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap_piece);
+
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_reserve_cow_range_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_allocate_cow_range_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_cancel_cow_range_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_end_cow_error);
+
+DEFINE_COW_EVENT(xfs_reflink_fork_buf);
+DEFINE_COW_EVENT(xfs_reflink_finish_fork_buf);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_fork_buf_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_finish_fork_buf_error);
+
+DEFINE_INODE_EVENT(xfs_reflink_cancel_pending_cow);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cancel_cow);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_cancel_pending_cow_error);
+
+/* rmap swapext tracepoints */
+DEFINE_INODE_IREC_EVENT(xfs_swap_extent_rmap_remap);
+DEFINE_INODE_IREC_EVENT(xfs_swap_extent_rmap_remap_piece);
+DEFINE_INODE_ERROR_EVENT(xfs_swap_extent_rmap_error);
+
 #endif /* _TRACE_XFS_H */
 
 #undef TRACE_INCLUDE_PATH
index e2bf86aad33dfaef40dd877ef12e32a7715cbabc..61b7fbdd3ebddfd1ebd20641753a87d292521508 100644 (file)
@@ -36,6 +36,11 @@ struct xfs_busy_extent;
 struct xfs_rud_log_item;
 struct xfs_rui_log_item;
 struct xfs_btree_cur;
+struct xfs_cui_log_item;
+struct xfs_cud_log_item;
+struct xfs_defer_ops;
+struct xfs_bui_log_item;
+struct xfs_bud_log_item;
 
 typedef struct xfs_log_item {
        struct list_head                li_ail;         /* AIL pointers */
@@ -248,4 +253,28 @@ int xfs_trans_log_finish_rmap_update(struct xfs_trans *tp,
                xfs_fsblock_t startblock, xfs_filblks_t blockcount,
                xfs_exntst_t state, struct xfs_btree_cur **pcur);
 
+/* refcount updates */
+enum xfs_refcount_intent_type;
+
+void xfs_refcount_update_init_defer_op(void);
+struct xfs_cud_log_item *xfs_trans_get_cud(struct xfs_trans *tp,
+               struct xfs_cui_log_item *cuip);
+int xfs_trans_log_finish_refcount_update(struct xfs_trans *tp,
+               struct xfs_cud_log_item *cudp, struct xfs_defer_ops *dfops,
+               enum xfs_refcount_intent_type type, xfs_fsblock_t startblock,
+               xfs_extlen_t blockcount, xfs_fsblock_t *new_fsb,
+               xfs_extlen_t *new_len, struct xfs_btree_cur **pcur);
+
+/* mapping updates */
+enum xfs_bmap_intent_type;
+
+void xfs_bmap_update_init_defer_op(void);
+struct xfs_bud_log_item *xfs_trans_get_bud(struct xfs_trans *tp,
+               struct xfs_bui_log_item *buip);
+int xfs_trans_log_finish_bmap_update(struct xfs_trans *tp,
+               struct xfs_bud_log_item *rudp, struct xfs_defer_ops *dfops,
+               enum xfs_bmap_intent_type type, struct xfs_inode *ip,
+               int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
+               xfs_filblks_t blockcount, xfs_exntst_t state);
+
 #endif /* __XFS_TRANS_H__ */
diff --git a/fs/xfs/xfs_trans_bmap.c b/fs/xfs/xfs_trans_bmap.c
new file mode 100644 (file)
index 0000000..6408e7d
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_bmap_item.h"
+#include "xfs_alloc.h"
+#include "xfs_bmap.h"
+#include "xfs_inode.h"
+
+/*
+ * This routine is called to allocate a "bmap update done"
+ * log item.
+ */
+struct xfs_bud_log_item *
+xfs_trans_get_bud(
+       struct xfs_trans                *tp,
+       struct xfs_bui_log_item         *buip)
+{
+       struct xfs_bud_log_item         *budp;
+
+       budp = xfs_bud_init(tp->t_mountp, buip);
+       xfs_trans_add_item(tp, &budp->bud_item);
+       return budp;
+}
+
+/*
+ * Finish an bmap update and log it to the BUD. Note that the
+ * transaction is marked dirty regardless of whether the bmap update
+ * succeeds or fails to support the BUI/BUD lifecycle rules.
+ */
+int
+xfs_trans_log_finish_bmap_update(
+       struct xfs_trans                *tp,
+       struct xfs_bud_log_item         *budp,
+       struct xfs_defer_ops            *dop,
+       enum xfs_bmap_intent_type       type,
+       struct xfs_inode                *ip,
+       int                             whichfork,
+       xfs_fileoff_t                   startoff,
+       xfs_fsblock_t                   startblock,
+       xfs_filblks_t                   blockcount,
+       xfs_exntst_t                    state)
+{
+       int                             error;
+
+       error = xfs_bmap_finish_one(tp, dop, ip, type, whichfork, startoff,
+                       startblock, blockcount, state);
+
+       /*
+        * Mark the transaction dirty, even on error. This ensures the
+        * transaction is aborted, which:
+        *
+        * 1.) releases the BUI and frees the BUD
+        * 2.) shuts down the filesystem
+        */
+       tp->t_flags |= XFS_TRANS_DIRTY;
+       budp->bud_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+
+       return error;
+}
+
+/* Sort bmap intents by inode. */
+static int
+xfs_bmap_update_diff_items(
+       void                            *priv,
+       struct list_head                *a,
+       struct list_head                *b)
+{
+       struct xfs_bmap_intent          *ba;
+       struct xfs_bmap_intent          *bb;
+
+       ba = container_of(a, struct xfs_bmap_intent, bi_list);
+       bb = container_of(b, struct xfs_bmap_intent, bi_list);
+       return ba->bi_owner->i_ino - bb->bi_owner->i_ino;
+}
+
+/* Get an BUI. */
+STATIC void *
+xfs_bmap_update_create_intent(
+       struct xfs_trans                *tp,
+       unsigned int                    count)
+{
+       struct xfs_bui_log_item         *buip;
+
+       ASSERT(count == XFS_BUI_MAX_FAST_EXTENTS);
+       ASSERT(tp != NULL);
+
+       buip = xfs_bui_init(tp->t_mountp);
+       ASSERT(buip != NULL);
+
+       /*
+        * Get a log_item_desc to point at the new item.
+        */
+       xfs_trans_add_item(tp, &buip->bui_item);
+       return buip;
+}
+
+/* Set the map extent flags for this mapping. */
+static void
+xfs_trans_set_bmap_flags(
+       struct xfs_map_extent           *bmap,
+       enum xfs_bmap_intent_type       type,
+       int                             whichfork,
+       xfs_exntst_t                    state)
+{
+       bmap->me_flags = 0;
+       switch (type) {
+       case XFS_BMAP_MAP:
+       case XFS_BMAP_UNMAP:
+               bmap->me_flags = type;
+               break;
+       default:
+               ASSERT(0);
+       }
+       if (state == XFS_EXT_UNWRITTEN)
+               bmap->me_flags |= XFS_BMAP_EXTENT_UNWRITTEN;
+       if (whichfork == XFS_ATTR_FORK)
+               bmap->me_flags |= XFS_BMAP_EXTENT_ATTR_FORK;
+}
+
+/* Log bmap updates in the intent item. */
+STATIC void
+xfs_bmap_update_log_item(
+       struct xfs_trans                *tp,
+       void                            *intent,
+       struct list_head                *item)
+{
+       struct xfs_bui_log_item         *buip = intent;
+       struct xfs_bmap_intent          *bmap;
+       uint                            next_extent;
+       struct xfs_map_extent           *map;
+
+       bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+
+       tp->t_flags |= XFS_TRANS_DIRTY;
+       buip->bui_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+
+       /*
+        * atomic_inc_return gives us the value after the increment;
+        * we want to use it as an array index so we need to subtract 1 from
+        * it.
+        */
+       next_extent = atomic_inc_return(&buip->bui_next_extent) - 1;
+       ASSERT(next_extent < buip->bui_format.bui_nextents);
+       map = &buip->bui_format.bui_extents[next_extent];
+       map->me_owner = bmap->bi_owner->i_ino;
+       map->me_startblock = bmap->bi_bmap.br_startblock;
+       map->me_startoff = bmap->bi_bmap.br_startoff;
+       map->me_len = bmap->bi_bmap.br_blockcount;
+       xfs_trans_set_bmap_flags(map, bmap->bi_type, bmap->bi_whichfork,
+                       bmap->bi_bmap.br_state);
+}
+
+/* Get an BUD so we can process all the deferred rmap updates. */
+STATIC void *
+xfs_bmap_update_create_done(
+       struct xfs_trans                *tp,
+       void                            *intent,
+       unsigned int                    count)
+{
+       return xfs_trans_get_bud(tp, intent);
+}
+
+/* Process a deferred rmap update. */
+STATIC int
+xfs_bmap_update_finish_item(
+       struct xfs_trans                *tp,
+       struct xfs_defer_ops            *dop,
+       struct list_head                *item,
+       void                            *done_item,
+       void                            **state)
+{
+       struct xfs_bmap_intent          *bmap;
+       int                             error;
+
+       bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+       error = xfs_trans_log_finish_bmap_update(tp, done_item, dop,
+                       bmap->bi_type,
+                       bmap->bi_owner, bmap->bi_whichfork,
+                       bmap->bi_bmap.br_startoff,
+                       bmap->bi_bmap.br_startblock,
+                       bmap->bi_bmap.br_blockcount,
+                       bmap->bi_bmap.br_state);
+       kmem_free(bmap);
+       return error;
+}
+
+/* Abort all pending BUIs. */
+STATIC void
+xfs_bmap_update_abort_intent(
+       void                            *intent)
+{
+       xfs_bui_release(intent);
+}
+
+/* Cancel a deferred rmap update. */
+STATIC void
+xfs_bmap_update_cancel_item(
+       struct list_head                *item)
+{
+       struct xfs_bmap_intent          *bmap;
+
+       bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+       kmem_free(bmap);
+}
+
+static const struct xfs_defer_op_type xfs_bmap_update_defer_type = {
+       .type           = XFS_DEFER_OPS_TYPE_BMAP,
+       .max_items      = XFS_BUI_MAX_FAST_EXTENTS,
+       .diff_items     = xfs_bmap_update_diff_items,
+       .create_intent  = xfs_bmap_update_create_intent,
+       .abort_intent   = xfs_bmap_update_abort_intent,
+       .log_item       = xfs_bmap_update_log_item,
+       .create_done    = xfs_bmap_update_create_done,
+       .finish_item    = xfs_bmap_update_finish_item,
+       .cancel_item    = xfs_bmap_update_cancel_item,
+};
+
+/* Register the deferred op type. */
+void
+xfs_bmap_update_init_defer_op(void)
+{
+       xfs_defer_init_op_type(&xfs_bmap_update_defer_type);
+}
diff --git a/fs/xfs/xfs_trans_refcount.c b/fs/xfs/xfs_trans_refcount.c
new file mode 100644 (file)
index 0000000..94c1877
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.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 would 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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_refcount_item.h"
+#include "xfs_alloc.h"
+#include "xfs_refcount.h"
+
+/*
+ * This routine is called to allocate a "refcount update done"
+ * log item.
+ */
+struct xfs_cud_log_item *
+xfs_trans_get_cud(
+       struct xfs_trans                *tp,
+       struct xfs_cui_log_item         *cuip)
+{
+       struct xfs_cud_log_item         *cudp;
+
+       cudp = xfs_cud_init(tp->t_mountp, cuip);
+       xfs_trans_add_item(tp, &cudp->cud_item);
+       return cudp;
+}
+
+/*
+ * Finish an refcount update and log it to the CUD. Note that the
+ * transaction is marked dirty regardless of whether the refcount
+ * update succeeds or fails to support the CUI/CUD lifecycle rules.
+ */
+int
+xfs_trans_log_finish_refcount_update(
+       struct xfs_trans                *tp,
+       struct xfs_cud_log_item         *cudp,
+       struct xfs_defer_ops            *dop,
+       enum xfs_refcount_intent_type   type,
+       xfs_fsblock_t                   startblock,
+       xfs_extlen_t                    blockcount,
+       xfs_fsblock_t                   *new_fsb,
+       xfs_extlen_t                    *new_len,
+       struct xfs_btree_cur            **pcur)
+{
+       int                             error;
+
+       error = xfs_refcount_finish_one(tp, dop, type, startblock,
+                       blockcount, new_fsb, new_len, pcur);
+
+       /*
+        * Mark the transaction dirty, even on error. This ensures the
+        * transaction is aborted, which:
+        *
+        * 1.) releases the CUI and frees the CUD
+        * 2.) shuts down the filesystem
+        */
+       tp->t_flags |= XFS_TRANS_DIRTY;
+       cudp->cud_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+
+       return error;
+}
+
+/* Sort refcount intents by AG. */
+static int
+xfs_refcount_update_diff_items(
+       void                            *priv,
+       struct list_head                *a,
+       struct list_head                *b)
+{
+       struct xfs_mount                *mp = priv;
+       struct xfs_refcount_intent      *ra;
+       struct xfs_refcount_intent      *rb;
+
+       ra = container_of(a, struct xfs_refcount_intent, ri_list);
+       rb = container_of(b, struct xfs_refcount_intent, ri_list);
+       return  XFS_FSB_TO_AGNO(mp, ra->ri_startblock) -
+               XFS_FSB_TO_AGNO(mp, rb->ri_startblock);
+}
+
+/* Get an CUI. */
+STATIC void *
+xfs_refcount_update_create_intent(
+       struct xfs_trans                *tp,
+       unsigned int                    count)
+{
+       struct xfs_cui_log_item         *cuip;
+
+       ASSERT(tp != NULL);
+       ASSERT(count > 0);
+
+       cuip = xfs_cui_init(tp->t_mountp, count);
+       ASSERT(cuip != NULL);
+
+       /*
+        * Get a log_item_desc to point at the new item.
+        */
+       xfs_trans_add_item(tp, &cuip->cui_item);
+       return cuip;
+}
+
+/* Set the phys extent flags for this reverse mapping. */
+static void
+xfs_trans_set_refcount_flags(
+       struct xfs_phys_extent          *refc,
+       enum xfs_refcount_intent_type   type)
+{
+       refc->pe_flags = 0;
+       switch (type) {
+       case XFS_REFCOUNT_INCREASE:
+       case XFS_REFCOUNT_DECREASE:
+       case XFS_REFCOUNT_ALLOC_COW:
+       case XFS_REFCOUNT_FREE_COW:
+               refc->pe_flags |= type;
+               break;
+       default:
+               ASSERT(0);
+       }
+}
+
+/* Log refcount updates in the intent item. */
+STATIC void
+xfs_refcount_update_log_item(
+       struct xfs_trans                *tp,
+       void                            *intent,
+       struct list_head                *item)
+{
+       struct xfs_cui_log_item         *cuip = intent;
+       struct xfs_refcount_intent      *refc;
+       uint                            next_extent;
+       struct xfs_phys_extent          *ext;
+
+       refc = container_of(item, struct xfs_refcount_intent, ri_list);
+
+       tp->t_flags |= XFS_TRANS_DIRTY;
+       cuip->cui_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+
+       /*
+        * atomic_inc_return gives us the value after the increment;
+        * we want to use it as an array index so we need to subtract 1 from
+        * it.
+        */
+       next_extent = atomic_inc_return(&cuip->cui_next_extent) - 1;
+       ASSERT(next_extent < cuip->cui_format.cui_nextents);
+       ext = &cuip->cui_format.cui_extents[next_extent];
+       ext->pe_startblock = refc->ri_startblock;
+       ext->pe_len = refc->ri_blockcount;
+       xfs_trans_set_refcount_flags(ext, refc->ri_type);
+}
+
+/* Get an CUD so we can process all the deferred refcount updates. */
+STATIC void *
+xfs_refcount_update_create_done(
+       struct xfs_trans                *tp,
+       void                            *intent,
+       unsigned int                    count)
+{
+       return xfs_trans_get_cud(tp, intent);
+}
+
+/* Process a deferred refcount update. */
+STATIC int
+xfs_refcount_update_finish_item(
+       struct xfs_trans                *tp,
+       struct xfs_defer_ops            *dop,
+       struct list_head                *item,
+       void                            *done_item,
+       void                            **state)
+{
+       struct xfs_refcount_intent      *refc;
+       xfs_fsblock_t                   new_fsb;
+       xfs_extlen_t                    new_aglen;
+       int                             error;
+
+       refc = container_of(item, struct xfs_refcount_intent, ri_list);
+       error = xfs_trans_log_finish_refcount_update(tp, done_item, dop,
+                       refc->ri_type,
+                       refc->ri_startblock,
+                       refc->ri_blockcount,
+                       &new_fsb, &new_aglen,
+                       (struct xfs_btree_cur **)state);
+       /* Did we run out of reservation?  Requeue what we didn't finish. */
+       if (!error && new_aglen > 0) {
+               ASSERT(refc->ri_type == XFS_REFCOUNT_INCREASE ||
+                      refc->ri_type == XFS_REFCOUNT_DECREASE);
+               refc->ri_startblock = new_fsb;
+               refc->ri_blockcount = new_aglen;
+               return -EAGAIN;
+       }
+       kmem_free(refc);
+       return error;
+}
+
+/* Clean up after processing deferred refcounts. */
+STATIC void
+xfs_refcount_update_finish_cleanup(
+       struct xfs_trans        *tp,
+       void                    *state,
+       int                     error)
+{
+       struct xfs_btree_cur    *rcur = state;
+
+       xfs_refcount_finish_one_cleanup(tp, rcur, error);
+}
+
+/* Abort all pending CUIs. */
+STATIC void
+xfs_refcount_update_abort_intent(
+       void                            *intent)
+{
+       xfs_cui_release(intent);
+}
+
+/* Cancel a deferred refcount update. */
+STATIC void
+xfs_refcount_update_cancel_item(
+       struct list_head                *item)
+{
+       struct xfs_refcount_intent      *refc;
+
+       refc = container_of(item, struct xfs_refcount_intent, ri_list);
+       kmem_free(refc);
+}
+
+static const struct xfs_defer_op_type xfs_refcount_update_defer_type = {
+       .type           = XFS_DEFER_OPS_TYPE_REFCOUNT,
+       .max_items      = XFS_CUI_MAX_FAST_EXTENTS,
+       .diff_items     = xfs_refcount_update_diff_items,
+       .create_intent  = xfs_refcount_update_create_intent,
+       .abort_intent   = xfs_refcount_update_abort_intent,
+       .log_item       = xfs_refcount_update_log_item,
+       .create_done    = xfs_refcount_update_create_done,
+       .finish_item    = xfs_refcount_update_finish_item,
+       .finish_cleanup = xfs_refcount_update_finish_cleanup,
+       .cancel_item    = xfs_refcount_update_cancel_item,
+};
+
+/* Register the deferred op type. */
+void
+xfs_refcount_update_init_defer_op(void)
+{
+       xfs_defer_init_op_type(&xfs_refcount_update_defer_type);
+}
index 5a50ef881568024fb4f7e543ac7e1b1272df0a50..9ead064b5e90ae34507c450cfa24988c8bc669c1 100644 (file)
@@ -48,12 +48,21 @@ xfs_trans_set_rmap_flags(
        case XFS_RMAP_MAP:
                rmap->me_flags |= XFS_RMAP_EXTENT_MAP;
                break;
+       case XFS_RMAP_MAP_SHARED:
+               rmap->me_flags |= XFS_RMAP_EXTENT_MAP_SHARED;
+               break;
        case XFS_RMAP_UNMAP:
                rmap->me_flags |= XFS_RMAP_EXTENT_UNMAP;
                break;
+       case XFS_RMAP_UNMAP_SHARED:
+               rmap->me_flags |= XFS_RMAP_EXTENT_UNMAP_SHARED;
+               break;
        case XFS_RMAP_CONVERT:
                rmap->me_flags |= XFS_RMAP_EXTENT_CONVERT;
                break;
+       case XFS_RMAP_CONVERT_SHARED:
+               rmap->me_flags |= XFS_RMAP_EXTENT_CONVERT_SHARED;
+               break;
        case XFS_RMAP_ALLOC:
                rmap->me_flags |= XFS_RMAP_EXTENT_ALLOC;
                break;
diff --git a/include/asm-generic/export.h b/include/asm-generic/export.h
new file mode 100644 (file)
index 0000000..43199a0
--- /dev/null
@@ -0,0 +1,94 @@
+#ifndef __ASM_GENERIC_EXPORT_H
+#define __ASM_GENERIC_EXPORT_H
+
+#ifndef KSYM_FUNC
+#define KSYM_FUNC(x) x
+#endif
+#ifdef CONFIG_64BIT
+#define __put .quad
+#ifndef KSYM_ALIGN
+#define KSYM_ALIGN 8
+#endif
+#ifndef KCRC_ALIGN
+#define KCRC_ALIGN 8
+#endif
+#else
+#define __put .long
+#ifndef KSYM_ALIGN
+#define KSYM_ALIGN 4
+#endif
+#ifndef KCRC_ALIGN
+#define KCRC_ALIGN 4
+#endif
+#endif
+
+#ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX
+#define KSYM(name) _##name
+#else
+#define KSYM(name) name
+#endif
+
+/*
+ * note on .section use: @progbits vs %progbits nastiness doesn't matter,
+ * since we immediately emit into those sections anyway.
+ */
+.macro ___EXPORT_SYMBOL name,val,sec
+#ifdef CONFIG_MODULES
+       .globl KSYM(__ksymtab_\name)
+       .section ___ksymtab\sec+\name,"a"
+       .balign KSYM_ALIGN
+KSYM(__ksymtab_\name):
+       __put \val, KSYM(__kstrtab_\name)
+       .previous
+       .section __ksymtab_strings,"a"
+KSYM(__kstrtab_\name):
+#ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX
+       .asciz "_\name"
+#else
+       .asciz "\name"
+#endif
+       .previous
+#ifdef CONFIG_MODVERSIONS
+       .section ___kcrctab\sec+\name,"a"
+       .balign KCRC_ALIGN
+KSYM(__kcrctab_\name):
+       __put KSYM(__crc_\name)
+       .weak KSYM(__crc_\name)
+       .previous
+#endif
+#endif
+.endm
+#undef __put
+
+#if defined(__KSYM_DEPS__)
+
+#define __EXPORT_SYMBOL(sym, val, sec) === __KSYM_##sym ===
+
+#elif defined(CONFIG_TRIM_UNUSED_KSYMS)
+
+#include <linux/kconfig.h>
+#include <generated/autoksyms.h>
+
+#define __EXPORT_SYMBOL(sym, val, sec)                         \
+       __cond_export_sym(sym, val, sec, config_enabled(__KSYM_##sym))
+#define __cond_export_sym(sym, val, sec, conf)                 \
+       ___cond_export_sym(sym, val, sec, conf)
+#define ___cond_export_sym(sym, val, sec, enabled)             \
+       __cond_export_sym_##enabled(sym, val, sec)
+#define __cond_export_sym_1(sym, val, sec) ___EXPORT_SYMBOL sym, val, sec
+#define __cond_export_sym_0(sym, val, sec) /* nothing */
+
+#else
+#define __EXPORT_SYMBOL(sym, val, sec) ___EXPORT_SYMBOL sym, val, sec
+#endif
+
+#define EXPORT_SYMBOL(name)                                    \
+       __EXPORT_SYMBOL(name, KSYM_FUNC(KSYM(name)),)
+#define EXPORT_SYMBOL_GPL(name)                                \
+       __EXPORT_SYMBOL(name, KSYM_FUNC(KSYM(name)), _gpl)
+#define EXPORT_DATA_SYMBOL(name)                               \
+       __EXPORT_SYMBOL(name, KSYM(name),)
+#define EXPORT_DATA_SYMBOL_GPL(name)                           \
+       __EXPORT_SYMBOL(name, KSYM(name),_gpl)
+
+#endif
diff --git a/include/asm-generic/libata-portmap.h b/include/asm-generic/libata-portmap.h
deleted file mode 100644 (file)
index cf14f2f..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_GENERIC_LIBATA_PORTMAP_H
-#define __ASM_GENERIC_LIBATA_PORTMAP_H
-
-#define ATA_PRIMARY_IRQ(dev)   14
-#define ATA_SECONDARY_IRQ(dev) 15
-
-#endif
index 4d9f233c4ba8343504b977e0273cc9a04cbbedb8..40e887068da213e595951f16caf8eb7ca83d3179 100644 (file)
@@ -65,6 +65,11 @@ extern void setup_per_cpu_areas(void);
 #define PER_CPU_DEF_ATTRIBUTES
 #endif
 
+#define raw_cpu_generic_read(pcp)                                      \
+({                                                                     \
+       *raw_cpu_ptr(&(pcp));                                           \
+})
+
 #define raw_cpu_generic_to_op(pcp, val, op)                            \
 do {                                                                   \
        *raw_cpu_ptr(&(pcp)) op val;                                    \
@@ -72,34 +77,39 @@ do {                                                                        \
 
 #define raw_cpu_generic_add_return(pcp, val)                           \
 ({                                                                     \
-       raw_cpu_add(pcp, val);                                          \
-       raw_cpu_read(pcp);                                              \
+       typeof(&(pcp)) __p = raw_cpu_ptr(&(pcp));                       \
+                                                                       \
+       *__p += val;                                                    \
+       *__p;                                                           \
 })
 
 #define raw_cpu_generic_xchg(pcp, nval)                                        \
 ({                                                                     \
+       typeof(&(pcp)) __p = raw_cpu_ptr(&(pcp));                       \
        typeof(pcp) __ret;                                              \
-       __ret = raw_cpu_read(pcp);                                      \
-       raw_cpu_write(pcp, nval);                                       \
+       __ret = *__p;                                                   \
+       *__p = nval;                                                    \
        __ret;                                                          \
 })
 
 #define raw_cpu_generic_cmpxchg(pcp, oval, nval)                       \
 ({                                                                     \
+       typeof(&(pcp)) __p = raw_cpu_ptr(&(pcp));                       \
        typeof(pcp) __ret;                                              \
-       __ret = raw_cpu_read(pcp);                                      \
+       __ret = *__p;                                                   \
        if (__ret == (oval))                                            \
-               raw_cpu_write(pcp, nval);                               \
+               *__p = nval;                                            \
        __ret;                                                          \
 })
 
 #define raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \
 ({                                                                     \
+       typeof(&(pcp1)) __p1 = raw_cpu_ptr(&(pcp1));                    \
+       typeof(&(pcp2)) __p2 = raw_cpu_ptr(&(pcp2));                    \
        int __ret = 0;                                                  \
-       if (raw_cpu_read(pcp1) == (oval1) &&                            \
-                        raw_cpu_read(pcp2)  == (oval2)) {              \
-               raw_cpu_write(pcp1, nval1);                             \
-               raw_cpu_write(pcp2, nval2);                             \
+       if (*__p1 == (oval1) && *__p2  == (oval2)) {                    \
+               *__p1 = nval1;                                          \
+               *__p2 = nval2;                                          \
                __ret = 1;                                              \
        }                                                               \
        (__ret);                                                        \
@@ -109,7 +119,7 @@ do {                                                                        \
 ({                                                                     \
        typeof(pcp) __ret;                                              \
        preempt_disable();                                              \
-       __ret = *this_cpu_ptr(&(pcp));                                  \
+       __ret = raw_cpu_generic_read(pcp);                              \
        preempt_enable();                                               \
        __ret;                                                          \
 })
@@ -118,17 +128,17 @@ do {                                                                      \
 do {                                                                   \
        unsigned long __flags;                                          \
        raw_local_irq_save(__flags);                                    \
-       *raw_cpu_ptr(&(pcp)) op val;                                    \
+       raw_cpu_generic_to_op(pcp, val, op);                            \
        raw_local_irq_restore(__flags);                                 \
 } while (0)
 
+
 #define this_cpu_generic_add_return(pcp, val)                          \
 ({                                                                     \
        typeof(pcp) __ret;                                              \
        unsigned long __flags;                                          \
        raw_local_irq_save(__flags);                                    \
-       raw_cpu_add(pcp, val);                                          \
-       __ret = raw_cpu_read(pcp);                                      \
+       __ret = raw_cpu_generic_add_return(pcp, val);                   \
        raw_local_irq_restore(__flags);                                 \
        __ret;                                                          \
 })
@@ -138,8 +148,7 @@ do {                                                                        \
        typeof(pcp) __ret;                                              \
        unsigned long __flags;                                          \
        raw_local_irq_save(__flags);                                    \
-       __ret = raw_cpu_read(pcp);                                      \
-       raw_cpu_write(pcp, nval);                                       \
+       __ret = raw_cpu_generic_xchg(pcp, nval);                        \
        raw_local_irq_restore(__flags);                                 \
        __ret;                                                          \
 })
@@ -149,9 +158,7 @@ do {                                                                        \
        typeof(pcp) __ret;                                              \
        unsigned long __flags;                                          \
        raw_local_irq_save(__flags);                                    \
-       __ret = raw_cpu_read(pcp);                                      \
-       if (__ret == (oval))                                            \
-               raw_cpu_write(pcp, nval);                               \
+       __ret = raw_cpu_generic_cmpxchg(pcp, oval, nval);               \
        raw_local_irq_restore(__flags);                                 \
        __ret;                                                          \
 })
@@ -168,16 +175,16 @@ do {                                                                      \
 })
 
 #ifndef raw_cpu_read_1
-#define raw_cpu_read_1(pcp)            (*raw_cpu_ptr(&(pcp)))
+#define raw_cpu_read_1(pcp)            raw_cpu_generic_read(pcp)
 #endif
 #ifndef raw_cpu_read_2
-#define raw_cpu_read_2(pcp)            (*raw_cpu_ptr(&(pcp)))
+#define raw_cpu_read_2(pcp)            raw_cpu_generic_read(pcp)
 #endif
 #ifndef raw_cpu_read_4
-#define raw_cpu_read_4(pcp)            (*raw_cpu_ptr(&(pcp)))
+#define raw_cpu_read_4(pcp)            raw_cpu_generic_read(pcp)
 #endif
 #ifndef raw_cpu_read_8
-#define raw_cpu_read_8(pcp)            (*raw_cpu_ptr(&(pcp)))
+#define raw_cpu_read_8(pcp)            raw_cpu_generic_read(pcp)
 #endif
 
 #ifndef raw_cpu_write_1
index 3e42bcdd014b45b66b95853ee699937666b11b3f..30747960bc54a29496eb134ebff26bfcb67e2fd7 100644 (file)
        *(.dtb.init.rodata)                                             \
        VMLINUX_SYMBOL(__dtb_end) = .;
 
-/* .data section */
+/*
+ * .data section
+ * LD_DEAD_CODE_DATA_ELIMINATION option enables -fdata-sections generates
+ * .data.identifier which needs to be pulled in with .data, but don't want to
+ * pull in .data..stuff which has its own requirements. Same for bss.
+ */
 #define DATA_DATA                                                      \
-       *(.data)                                                        \
+       *(.data .data.[0-9a-zA-Z_]*)                                    \
        *(.ref.data)                                                    \
        *(.data..shared_aligned) /* percpu related */                   \
        MEM_KEEP(init.data)                                             \
        /* Kernel symbol table: Normal symbols */                       \
        __ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {         \
                VMLINUX_SYMBOL(__start___ksymtab) = .;                  \
-               *(SORT(___ksymtab+*))                                   \
+               KEEP(*(SORT(___ksymtab+*)))                             \
                VMLINUX_SYMBOL(__stop___ksymtab) = .;                   \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only symbols */                     \
        __ksymtab_gpl     : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) {     \
                VMLINUX_SYMBOL(__start___ksymtab_gpl) = .;              \
-               *(SORT(___ksymtab_gpl+*))                               \
+               KEEP(*(SORT(___ksymtab_gpl+*)))                         \
                VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .;               \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal unused symbols */                \
        __ksymtab_unused  : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) {  \
                VMLINUX_SYMBOL(__start___ksymtab_unused) = .;           \
-               *(SORT(___ksymtab_unused+*))                            \
+               KEEP(*(SORT(___ksymtab_unused+*)))                      \
                VMLINUX_SYMBOL(__stop___ksymtab_unused) = .;            \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only unused symbols */              \
        __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___ksymtab_unused_gpl) = .;       \
-               *(SORT(___ksymtab_unused_gpl+*))                        \
+               KEEP(*(SORT(___ksymtab_unused_gpl+*)))                  \
                VMLINUX_SYMBOL(__stop___ksymtab_unused_gpl) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-future-only symbols */              \
        __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___ksymtab_gpl_future) = .;       \
-               *(SORT(___ksymtab_gpl_future+*))                        \
+               KEEP(*(SORT(___ksymtab_gpl_future+*)))                  \
                VMLINUX_SYMBOL(__stop___ksymtab_gpl_future) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal symbols */                       \
        __kcrctab         : AT(ADDR(__kcrctab) - LOAD_OFFSET) {         \
                VMLINUX_SYMBOL(__start___kcrctab) = .;                  \
-               *(SORT(___kcrctab+*))                                   \
+               KEEP(*(SORT(___kcrctab+*)))                             \
                VMLINUX_SYMBOL(__stop___kcrctab) = .;                   \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only symbols */                     \
        __kcrctab_gpl     : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) {     \
                VMLINUX_SYMBOL(__start___kcrctab_gpl) = .;              \
-               *(SORT(___kcrctab_gpl+*))                               \
+               KEEP(*(SORT(___kcrctab_gpl+*)))                         \
                VMLINUX_SYMBOL(__stop___kcrctab_gpl) = .;               \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal unused symbols */                \
        __kcrctab_unused  : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) {  \
                VMLINUX_SYMBOL(__start___kcrctab_unused) = .;           \
-               *(SORT(___kcrctab_unused+*))                            \
+               KEEP(*(SORT(___kcrctab_unused+*)))                      \
                VMLINUX_SYMBOL(__stop___kcrctab_unused) = .;            \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only unused symbols */              \
        __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___kcrctab_unused_gpl) = .;       \
-               *(SORT(___kcrctab_unused_gpl+*))                        \
+               KEEP(*(SORT(___kcrctab_unused_gpl+*)))                  \
                VMLINUX_SYMBOL(__stop___kcrctab_unused_gpl) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-future-only symbols */              \
        __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___kcrctab_gpl_future) = .;       \
-               *(SORT(___kcrctab_gpl_future+*))                        \
+               KEEP(*(SORT(___kcrctab_gpl_future+*)))                  \
                VMLINUX_SYMBOL(__stop___kcrctab_gpl_future) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: strings */                              \
         __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) {        \
-               *(__ksymtab_strings)                                    \
+               KEEP(*(__ksymtab_strings))                              \
        }                                                               \
                                                                        \
        /* __*init sections */                                          \
 #define SECURITY_INIT                                                  \
        .security_initcall.init : AT(ADDR(.security_initcall.init) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__security_initcall_start) = .;          \
-               *(.security_initcall.init)                              \
+               KEEP(*(.security_initcall.init))                        \
                VMLINUX_SYMBOL(__security_initcall_end) = .;            \
        }
 
 /* .text section. Map to function alignment to avoid address changes
- * during second ld run in second ld pass when generating System.map */
+ * during second ld run in second ld pass when generating System.map
+ * LD_DEAD_CODE_DATA_ELIMINATION option enables -ffunction-sections generates
+ * .text.identifier which needs to be pulled in with .text , but some
+ * architectures define .text.foo which is not intended to be pulled in here.
+ * Those enabling LD_DEAD_CODE_DATA_ELIMINATION must ensure they don't have
+ * conflicting section names, and must pull in .text.[0-9a-zA-Z_]* */
 #define TEXT_TEXT                                                      \
                ALIGN_FUNCTION();                                       \
                *(.text.hot .text .text.fixup .text.unlikely)           \
 
 /* init and exit section handling */
 #define INIT_DATA                                                      \
+       KEEP(*(SORT(___kentry+*)))                                      \
        *(.init.data)                                                   \
        MEM_DISCARD(init.data)                                          \
        KERNEL_CTORS()                                                  \
                BSS_FIRST_SECTIONS                                      \
                *(.bss..page_aligned)                                   \
                *(.dynbss)                                              \
-               *(.bss)                                                 \
+               *(.bss .bss.[0-9a-zA-Z_]*)                              \
                *(COMMON)                                               \
        }
 
 
 #define INIT_CALLS_LEVEL(level)                                                \
                VMLINUX_SYMBOL(__initcall##level##_start) = .;          \
-               *(.initcall##level##.init)                              \
-               *(.initcall##level##s.init)                             \
+               KEEP(*(.initcall##level##.init))                        \
+               KEEP(*(.initcall##level##s.init))                       \
 
 #define INIT_CALLS                                                     \
                VMLINUX_SYMBOL(__initcall_start) = .;                   \
-               *(.initcallearly.init)                                  \
+               KEEP(*(.initcallearly.init))                            \
                INIT_CALLS_LEVEL(0)                                     \
                INIT_CALLS_LEVEL(1)                                     \
                INIT_CALLS_LEVEL(2)                                     \
 
 #define CON_INITCALL                                                   \
                VMLINUX_SYMBOL(__con_initcall_start) = .;               \
-               *(.con_initcall.init)                                   \
+               KEEP(*(.con_initcall.init))                             \
                VMLINUX_SYMBOL(__con_initcall_end) = .;
 
 #define SECURITY_INITCALL                                              \
                VMLINUX_SYMBOL(__security_initcall_start) = .;          \
-               *(.security_initcall.init)                              \
+               KEEP(*(.security_initcall.init))                        \
                VMLINUX_SYMBOL(__security_initcall_end) = .;
 
 #ifdef CONFIG_BLK_DEV_INITRD
 #define INIT_RAM_FS                                                    \
        . = ALIGN(4);                                                   \
        VMLINUX_SYMBOL(__initramfs_start) = .;                          \
-       *(.init.ramfs)                                                  \
+       KEEP(*(.init.ramfs))                                            \
        . = ALIGN(8);                                                   \
-       *(.init.ramfs.info)
+       KEEP(*(.init.ramfs.info))
 #else
 #define INIT_RAM_FS
 #endif
index 729ab9fc325e2a5d728d929fdc8b3332d4867eef..2a99f1d52bb5e778648ebb0882bc6fb1d9f4eb2c 100644 (file)
@@ -11,4 +11,9 @@
 #define TEGRA124_SOCTHERM_SENSOR_PLLX 3
 #define TEGRA124_SOCTHERM_SENSOR_NUM 4
 
+#define TEGRA_SOCTHERM_THROT_LEVEL_LOW  0
+#define TEGRA_SOCTHERM_THROT_LEVEL_MED  1
+#define TEGRA_SOCTHERM_THROT_LEVEL_HIGH 2
+#define TEGRA_SOCTHERM_THROT_LEVEL_NONE -1
+
 #endif
index 94afcb2c384cf2d7d184742e870c0becfeffe312..ddbeda6dbdc87b4121617631efa0f1a486d1b6e2 100644 (file)
@@ -946,9 +946,17 @@ struct acpi_reference_args {
 #ifdef CONFIG_ACPI
 int acpi_dev_get_property(struct acpi_device *adev, const char *name,
                          acpi_object_type type, const union acpi_object **obj);
-int acpi_node_get_property_reference(struct fwnode_handle *fwnode,
-                                    const char *name, size_t index,
-                                    struct acpi_reference_args *args);
+int __acpi_node_get_property_reference(struct fwnode_handle *fwnode,
+                               const char *name, size_t index, size_t num_args,
+                               struct acpi_reference_args *args);
+
+static inline int acpi_node_get_property_reference(struct fwnode_handle *fwnode,
+                               const char *name, size_t index,
+                               struct acpi_reference_args *args)
+{
+       return __acpi_node_get_property_reference(fwnode, name, index,
+               MAX_ACPI_REFERENCE_ARGS, args);
+}
 
 int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname,
                       void **valptr);
@@ -1024,6 +1032,14 @@ static inline int acpi_dev_get_property(struct acpi_device *adev,
        return -ENXIO;
 }
 
+static inline int
+__acpi_node_get_property_reference(struct fwnode_handle *fwnode,
+                               const char *name, size_t index, size_t num_args,
+                               struct acpi_reference_args *args)
+{
+       return -ENXIO;
+}
+
 static inline int acpi_node_get_property_reference(struct fwnode_handle *fwnode,
                                const char *name, size_t index,
                                struct acpi_reference_args *args)
index e82e3ee2c54abf368f3e3d5cd30908156c863479..1035879b322cb750aeac5de0a0c38542350514f5 100644 (file)
 #define CNTL_LDMAFIFOTIME      (1 << 15)
 #define CNTL_WATERMARK         (1 << 16)
 
+/* ST Microelectronics variant bits */
+#define CNTL_ST_1XBPP_444      0x0
+#define CNTL_ST_1XBPP_5551     (1 << 17)
+#define CNTL_ST_1XBPP_565      (1 << 18)
+#define CNTL_ST_CDWID_12       0x0
+#define CNTL_ST_CDWID_16       (1 << 19)
+#define CNTL_ST_CDWID_18       (1 << 20)
+#define CNTL_ST_CDWID_24       ((1 << 19)|(1 << 20))
+#define CNTL_ST_CEAEN          (1 << 21)
+#define CNTL_ST_LCDBPP24_PACKED        (6 << 1)
+
 enum {
        /* individual formats */
        CLCD_CAP_RGB444         = (1 << 0),
@@ -93,6 +104,8 @@ enum {
        CLCD_CAP_ALL            = CLCD_CAP_BGR | CLCD_CAP_RGB,
 };
 
+struct backlight_device;
+
 struct clcd_panel {
        struct fb_videomode     mode;
        signed short            width;  /* width in mm */
@@ -105,6 +118,13 @@ struct clcd_panel {
                                fixedtimings:1,
                                grayscale:1;
        unsigned int            connector;
+       struct backlight_device *backlight;
+       /*
+        * If the B/R lines are switched between the CLCD
+        * and the panel we need to know this and not try to
+        * compensate with the BGR bit in the control register.
+        */
+       bool                    bgr_connection;
 };
 
 struct clcd_regs {
@@ -170,11 +190,38 @@ struct clcd_board {
 struct amba_device;
 struct clk;
 
+/**
+ * struct clcd_vendor_data - holds hardware (IP-block) vendor-specific
+ * variant information
+ *
+ * @clock_timregs: the CLCD needs to be clocked when accessing the
+ * timer registers, or the hardware will hang.
+ * @packed_24_bit_pixels: this variant supports 24bit packed pixel data,
+ * so that RGB accesses 3 bytes at a time, not just on even 32bit
+ * boundaries, packing the pixel data in memory. ST Microelectronics
+ * have this.
+ * @st_bitmux_control: ST Microelectronics have implemented output
+ * bit line multiplexing into the CLCD control register. This indicates
+ * that we need to use this.
+ * @init_board: custom board init function for this variant
+ * @init_panel: custom panel init function for this variant
+ */
+struct clcd_vendor_data {
+       bool    clock_timregs;
+       bool    packed_24_bit_pixels;
+       bool    st_bitmux_control;
+       int     (*init_board)(struct amba_device *adev,
+                             struct clcd_board *board);
+       int     (*init_panel)(struct clcd_fb *fb,
+                             struct device_node *panel);
+};
+
 /* this data structure describes each frame buffer device we find */
 struct clcd_fb {
        struct fb_info          fb;
        struct amba_device      *dev;
        struct clk              *clk;
+       struct clcd_vendor_data *vendor;
        struct clcd_panel       *panel;
        struct clcd_board       *board;
        void                    *board_data;
@@ -231,16 +278,22 @@ static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs)
        if (var->grayscale)
                val |= CNTL_LCDBW;
 
-       if (fb->panel->caps && fb->board->caps &&
-           var->bits_per_pixel >= 16) {
+       if (fb->panel->caps && fb->board->caps && var->bits_per_pixel >= 16) {
                /*
                 * if board and panel supply capabilities, we can support
-                * changing BGR/RGB depending on supplied parameters
+                * changing BGR/RGB depending on supplied parameters. Here
+                * we switch to what the framebuffer is providing if need
+                * be, so if the framebuffer is BGR but the display connection
+                * is RGB (first case) we switch it around. Vice versa mutatis
+                * mutandis if the framebuffer is RGB but the display connection
+                * is BGR, we flip it around.
                 */
                if (var->red.offset == 0)
                        val &= ~CNTL_BGR;
                else
                        val |= CNTL_BGR;
+               if (fb->panel->bgr_connection)
+                       val ^= CNTL_BGR;
        }
 
        switch (var->bits_per_pixel) {
@@ -270,6 +323,10 @@ static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs)
                else
                        val |= CNTL_LCDBPP16_444;
                break;
+       case 24:
+               /* Modified variant supporting 24 bit packed pixels */
+               val |= CNTL_ST_LCDBPP24_PACKED;
+               break;
        case 32:
                val |= CNTL_LCDBPP24;
                break;
index adbc812c009b575d1006e494d390c18ac8c7f0b0..fdb180367ba134aaa8b54e372c44c8680161fd2f 100644 (file)
@@ -105,6 +105,7 @@ enum {
        ATA_ID_CFA_KEY_MGMT     = 162,
        ATA_ID_CFA_MODES        = 163,
        ATA_ID_DATA_SET_MGMT    = 169,
+       ATA_ID_SCT_CMD_XPORT    = 206,
        ATA_ID_ROT_SPEED        = 217,
        ATA_ID_PIO4             = (1 << 1),
 
@@ -788,6 +789,48 @@ static inline bool ata_id_sense_reporting_enabled(const u16 *id)
        return id[ATA_ID_COMMAND_SET_4] & (1 << 6);
 }
 
+/**
+ *
+ * Word: 206 - SCT Command Transport
+ *    15:12 - Vendor Specific
+ *     11:6 - Reserved
+ *        5 - SCT Command Transport Data Tables supported
+ *        4 - SCT Command Transport Features Control supported
+ *        3 - SCT Command Transport Error Recovery Control supported
+ *        2 - SCT Command Transport Write Same supported
+ *        1 - SCT Command Transport Long Sector Access supported
+ *        0 - SCT Command Transport supported
+ */
+static inline bool ata_id_sct_data_tables(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 5) ? true : false;
+}
+
+static inline bool ata_id_sct_features_ctrl(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 4) ? true : false;
+}
+
+static inline bool ata_id_sct_error_recovery_ctrl(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 3) ? true : false;
+}
+
+static inline bool ata_id_sct_write_same(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 2) ? true : false;
+}
+
+static inline bool ata_id_sct_long_sector_access(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 1) ? true : false;
+}
+
+static inline bool ata_id_sct_supported(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 0) ? true : false;
+}
+
 /**
  *     ata_id_major_version    -       get ATA level of drive
  *     @id: Identify data
@@ -1071,32 +1114,6 @@ static inline void ata_id_to_hd_driveid(u16 *id)
 #endif
 }
 
-/*
- * Write LBA Range Entries to the buffer that will cover the extent from
- * sector to sector + count.  This is used for TRIM and for ADD LBA(S)
- * TO NV CACHE PINNED SET.
- */
-static inline unsigned ata_set_lba_range_entries(void *_buffer,
-               unsigned num, u64 sector, unsigned long count)
-{
-       __le64 *buffer = _buffer;
-       unsigned i = 0, used_bytes;
-
-       while (i < num) {
-               u64 entry = sector |
-                       ((u64)(count > 0xffff ? 0xffff : count) << 48);
-               buffer[i++] = __cpu_to_le64(entry);
-               if (count <= 0xffff)
-                       break;
-               count -= 0xffff;
-               sector += 0xffff;
-       }
-
-       used_bytes = ALIGN(i * 8, 512);
-       memset(buffer + i, 0, used_bytes - i * 8);
-       return used_bytes;
-}
-
 static inline bool ata_ok(u8 status)
 {
        return ((status & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | ATA_ERR))
index cbdbf34de5b607e799bd9739e8815dff4037c76c..3bf5d33800ab61957cba76fcd0aec1815493f440 100644 (file)
@@ -343,16 +343,7 @@ static inline struct blkcg *cpd_to_blkcg(struct blkcg_policy_data *cpd)
  */
 static inline int blkg_path(struct blkcg_gq *blkg, char *buf, int buflen)
 {
-       char *p;
-
-       p = cgroup_path(blkg->blkcg->css.cgroup, buf, buflen);
-       if (!p) {
-               strncpy(buf, "<unavailable>", buflen);
-               return -ENAMETOOLONG;
-       }
-
-       memmove(buf, p, buf + buflen - p);
-       return 0;
+       return cgroup_path(blkg->blkcg->css.cgroup, buf, buflen);
 }
 
 /**
index 440a72164a11054d63f7a186b4f8ebff705e2e05..c83c23f0577bd908df08298dcbde74a7961dcf8c 100644 (file)
@@ -97,7 +97,7 @@ int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts);
 int cgroup_rm_cftypes(struct cftype *cfts);
 void cgroup_file_notify(struct cgroup_file *cfile);
 
-char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen);
+int task_cgroup_path(struct task_struct *task, char *buf, size_t buflen);
 int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry);
 int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
                     struct pid *pid, struct task_struct *tsk);
@@ -555,8 +555,7 @@ static inline int cgroup_name(struct cgroup *cgrp, char *buf, size_t buflen)
        return kernfs_name(cgrp->kn, buf, buflen);
 }
 
-static inline char * __must_check cgroup_path(struct cgroup *cgrp, char *buf,
-                                             size_t buflen)
+static inline int cgroup_path(struct cgroup *cgrp, char *buf, size_t buflen)
 {
        return kernfs_path(cgrp->kn, buf, buflen);
 }
@@ -658,8 +657,8 @@ struct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
                                        struct user_namespace *user_ns,
                                        struct cgroup_namespace *old_ns);
 
-char *cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
-                    struct cgroup_namespace *ns);
+int cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
+                  struct cgroup_namespace *ns);
 
 #else /* !CONFIG_CGROUPS */
 
index 573c5a18908fd53970fefea291805c500fd1d7f9..432f5c97e18f4f75fd68b1c1101cf828fb3d4fa0 100644 (file)
 #endif /* GCC_VERSION >= 40300 */
 
 #if GCC_VERSION >= 40500
+
+#ifndef __CHECKER__
+#ifdef LATENT_ENTROPY_PLUGIN
+#define __latent_entropy __attribute__((latent_entropy))
+#endif
+#endif
+
 /*
  * Mark a position in code as unreachable.  This can be used to
  * suppress control flow warnings after asm blocks that transfer
index 668569844d37cef4d51c6c7652d5f34ded2a66d6..cf0fa5d86059b6672773025b625027f6301074c1 100644 (file)
@@ -182,6 +182,29 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
 # define unreachable() do { } while (1)
 #endif
 
+/*
+ * KENTRY - kernel entry point
+ * This can be used to annotate symbols (functions or data) that are used
+ * without their linker symbol being referenced explicitly. For example,
+ * interrupt vector handlers, or functions in the kernel image that are found
+ * programatically.
+ *
+ * Not required for symbols exported with EXPORT_SYMBOL, or initcalls. Those
+ * are handled in their own way (with KEEP() in linker scripts).
+ *
+ * KENTRY can be avoided if the symbols in question are marked as KEEP() in the
+ * linker script. For example an architecture could KEEP() its entire
+ * boot/exception vector code rather than annotate each function and data.
+ */
+#ifndef KENTRY
+# define KENTRY(sym)                                           \
+       extern typeof(sym) sym;                                 \
+       static const unsigned long __kentry_##sym               \
+       __used                                                  \
+       __attribute__((section("___kentry" "+" #sym ), used))   \
+       = (unsigned long)&sym;
+#endif
+
 #ifndef RELOC_HIDE
 # define RELOC_HIDE(ptr, off)                                  \
   ({ unsigned long __ptr;                                      \
@@ -406,6 +429,10 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
 # define __attribute_const__   /* unimplemented */
 #endif
 
+#ifndef __latent_entropy
+# define __latent_entropy
+#endif
+
 /*
  * Tell gcc if a function is cold. The compiler will assume any path
  * directly leading to the call is unlikely.
index 631ba33bbe9fdb2fe0fd3f73cb1ab1773b52f617..5fa55fc56e18d1971da8c2e031c35a17fe0d51fc 100644 (file)
@@ -639,19 +639,19 @@ static inline int cpufreq_table_find_index_al(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq >= target_freq)
-                       return i;
+                       return pos - table;
 
-               best = i;
+               best = pos;
        }
 
-       return best;
+       return best - table;
 }
 
 /* Find lowest freq at or above target in a table in descending order */
@@ -659,28 +659,28 @@ static inline int cpufreq_table_find_index_dl(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq == target_freq)
-                       return i;
+                       return pos - table;
 
                if (freq > target_freq) {
-                       best = i;
+                       best = pos;
                        continue;
                }
 
                /* No freq found above target_freq */
-               if (best == -1)
-                       return i;
+               if (best == table - 1)
+                       return pos - table;
 
-               return best;
+               return best - pos;
        }
 
-       return best;
+       return best - pos;
 }
 
 /* Works only on sorted freq-tables */
@@ -700,28 +700,28 @@ static inline int cpufreq_table_find_index_ah(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq == target_freq)
-                       return i;
+                       return pos - table;
 
                if (freq < target_freq) {
-                       best = i;
+                       best = pos;
                        continue;
                }
 
                /* No freq found below target_freq */
-               if (best == -1)
-                       return i;
+               if (best == table - 1)
+                       return pos - table;
 
-               return best;
+               return best - table;
        }
 
-       return best;
+       return best - table;
 }
 
 /* Find highest freq at or below target in a table in descending order */
@@ -729,19 +729,19 @@ static inline int cpufreq_table_find_index_dh(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq <= target_freq)
-                       return i;
+                       return pos - table;
 
-               best = i;
+               best = pos;
        }
 
-       return best;
+       return best - table;
 }
 
 /* Works only on sorted freq-tables */
@@ -761,32 +761,32 @@ static inline int cpufreq_table_find_index_ac(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq == target_freq)
-                       return i;
+                       return pos - table;
 
                if (freq < target_freq) {
-                       best = i;
+                       best = pos;
                        continue;
                }
 
                /* No freq found below target_freq */
-               if (best == -1)
-                       return i;
+               if (best == table - 1)
+                       return pos - table;
 
                /* Choose the closest freq */
-               if (target_freq - table[best].frequency > freq - target_freq)
-                       return i;
+               if (target_freq - best->frequency > freq - target_freq)
+                       return pos - table;
 
-               return best;
+               return best - table;
        }
 
-       return best;
+       return best - table;
 }
 
 /* Find closest freq to target in a table in descending order */
@@ -794,32 +794,32 @@ static inline int cpufreq_table_find_index_dc(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq == target_freq)
-                       return i;
+                       return pos - table;
 
                if (freq > target_freq) {
-                       best = i;
+                       best = pos;
                        continue;
                }
 
                /* No freq found above target_freq */
-               if (best == -1)
-                       return i;
+               if (best == table - 1)
+                       return pos - table;
 
                /* Choose the closest freq */
-               if (table[best].frequency - target_freq > target_freq - freq)
-                       return i;
+               if (best->frequency - target_freq > target_freq - freq)
+                       return pos - table;
 
-               return best;
+               return best - table;
        }
 
-       return best;
+       return best - table;
 }
 
 /* Works only on sorted freq-tables */
index d7df4922da1d08843253086b144199fd299fd1c5..2a0f61fbc7310e61f5927c31250e208d217c3e26 100644 (file)
@@ -1,5 +1,6 @@
 #ifndef _LINUX_EXPORT_H
 #define _LINUX_EXPORT_H
+
 /*
  * Export symbols from the kernel to modules.  Forked from module.h
  * to reduce the amount of pointless cruft we feed to gcc when only
@@ -42,27 +43,26 @@ extern struct module __this_module;
 #ifdef CONFIG_MODVERSIONS
 /* Mark the CRC weak since genksyms apparently decides not to
  * generate a checksums for some symbols */
-#define __CRC_SYMBOL(sym, sec)                                 \
-       extern __visible void *__crc_##sym __attribute__((weak));               \
-       static const unsigned long __kcrctab_##sym              \
-       __used                                                  \
-       __attribute__((section("___kcrctab" sec "+" #sym), unused))     \
+#define __CRC_SYMBOL(sym, sec)                                         \
+       extern __visible void *__crc_##sym __attribute__((weak));       \
+       static const unsigned long __kcrctab_##sym                      \
+       __used                                                          \
+       __attribute__((section("___kcrctab" sec "+" #sym), used))       \
        = (unsigned long) &__crc_##sym;
 #else
 #define __CRC_SYMBOL(sym, sec)
 #endif
 
 /* For every exported symbol, place a struct in the __ksymtab section */
-#define ___EXPORT_SYMBOL(sym, sec)                             \
-       extern typeof(sym) sym;                                 \
-       __CRC_SYMBOL(sym, sec)                                  \
-       static const char __kstrtab_##sym[]                     \
-       __attribute__((section("__ksymtab_strings"), aligned(1))) \
-       = VMLINUX_SYMBOL_STR(sym);                              \
-       extern const struct kernel_symbol __ksymtab_##sym;      \
-       __visible const struct kernel_symbol __ksymtab_##sym    \
-       __used                                                  \
-       __attribute__((section("___ksymtab" sec "+" #sym), unused))     \
+#define ___EXPORT_SYMBOL(sym, sec)                                     \
+       extern typeof(sym) sym;                                         \
+       __CRC_SYMBOL(sym, sec)                                          \
+       static const char __kstrtab_##sym[]                             \
+       __attribute__((section("__ksymtab_strings"), aligned(1)))       \
+       = VMLINUX_SYMBOL_STR(sym);                                      \
+       static const struct kernel_symbol __ksymtab_##sym               \
+       __used                                                          \
+       __attribute__((section("___ksymtab" sec "+" #sym), used))       \
        = { (unsigned long)&sym, __kstrtab_##sym }
 
 #if defined(__KSYM_DEPS__)
index b03c0625fa6ec348327385cfbc730dd292ef81cc..5ab958cdc50bcb23b084f0faadb91b2793185fcd 100644 (file)
@@ -157,12 +157,13 @@ struct fid {
  *    @fh_to_dentry is given a &struct super_block (@sb) and a file handle
  *    fragment (@fh, @fh_len). It should return a &struct dentry which refers
  *    to the same file that the file handle fragment refers to.  If it cannot,
- *    it should return a %NULL pointer if the file was found but no acceptable
- *    &dentries were available, or an %ERR_PTR error code indicating why it
- *    couldn't be found (e.g. %ENOENT or %ENOMEM).  Any suitable dentry can be
- *    returned including, if necessary, a new dentry created with d_alloc_root.
- *    The caller can then find any other extant dentries by following the
- *    d_alias links.
+ *    it should return a %NULL pointer if the file cannot be found, or an
+ *    %ERR_PTR error code of %ENOMEM if a memory allocation failure occurred.
+ *    Any other error code is treated like %NULL, and will cause an %ESTALE error
+ *    for callers of exportfs_decode_fh().
+ *    Any suitable dentry can be returned including, if necessary, a new dentry
+ *    created with d_alloc_root.  The caller can then find any other extant
+ *    dentries by following the d_alias links.
  *
  * fh_to_parent:
  *    Same as @fh_to_dentry, except that it returns a pointer to the parent
index 996111000a8c014556ff5997e609aeb28bd5ec1d..7494dc67c66f421541edf064d69233bbbc432d8f 100644 (file)
@@ -25,6 +25,7 @@ struct space_resv {
                                         FALLOC_FL_PUNCH_HOLE |         \
                                         FALLOC_FL_COLLAPSE_RANGE |     \
                                         FALLOC_FL_ZERO_RANGE |         \
-                                        FALLOC_FL_INSERT_RANGE)
+                                        FALLOC_FL_INSERT_RANGE |       \
+                                        FALLOC_FL_UNSHARE_RANGE)
 
 #endif /* _FALLOC_H_ */
index aca2a6a1d0358f181264877965ff3e2ba882a8a0..6e84b2cae6ad62b529298b662856dc857c3091b6 100644 (file)
@@ -105,7 +105,7 @@ struct files_struct *get_files_struct(struct task_struct *);
 void put_files_struct(struct files_struct *fs);
 void reset_files_struct(struct files_struct *);
 int unshare_files(struct files_struct **);
-struct files_struct *dup_fd(struct files_struct *, int *);
+struct files_struct *dup_fd(struct files_struct *, int *) __latent_entropy;
 void do_close_on_exec(struct files_struct *);
 int iterate_fd(struct files_struct *, unsigned,
                int (*)(const void *, struct file *, unsigned),
index bc65d5918140d3ab1d077eb5ed8f4655a8586c25..16d2b6e874d679597478e653f3ef23974e0c33de 100644 (file)
@@ -2934,6 +2934,7 @@ extern int vfs_stat(const char __user *, struct kstat *);
 extern int vfs_lstat(const char __user *, struct kstat *);
 extern int vfs_fstat(unsigned int, struct kstat *);
 extern int vfs_fstatat(int , const char __user *, struct kstat *, int);
+extern const char *vfs_get_link(struct dentry *, struct delayed_call *);
 
 extern int __generic_block_fiemap(struct inode *inode,
                                  struct fiemap_extent_info *fieinfo,
index 1dbf52f9c24b88a0f4a299bae5ec3bfdcea1e566..e0341af6950e2116a43b3b0281f57fea8099c06f 100644 (file)
@@ -437,7 +437,7 @@ extern void disk_flush_events(struct gendisk *disk, unsigned int mask);
 extern unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask);
 
 /* drivers/char/random.c */
-extern void add_disk_randomness(struct gendisk *disk);
+extern void add_disk_randomness(struct gendisk *disk) __latent_entropy;
 extern void rand_initialize_disk(struct gendisk *disk);
 
 static inline sector_t get_start_sect(struct block_device *bdev)
index 5a3321a7909b92f489f17a2e59c9aec8c90e35ba..e30104ceb86dcb3076f7950d34d2776735b132ff 100644 (file)
@@ -39,7 +39,7 @@
 
 /* These are for everybody (although not all archs will actually
    discard it in modules) */
-#define __init         __section(.init.text) __cold notrace
+#define __init         __section(.init.text) __cold notrace __latent_entropy
 #define __initdata     __section(.init.data)
 #define __initconst    __section(.init.rodata)
 #define __exitdata     __section(.exit.data)
@@ -75,7 +75,8 @@
 #define __exit          __section(.exit.text) __exitused __cold notrace
 
 /* Used for MEMORY_HOTPLUG */
-#define __meminit        __section(.meminit.text) __cold notrace
+#define __meminit        __section(.meminit.text) __cold notrace \
+                                                 __latent_entropy
 #define __meminitdata    __section(.meminit.data)
 #define __meminitconst   __section(.meminit.rodata)
 #define __memexit        __section(.memexit.text) __exitused __cold notrace
@@ -139,24 +140,8 @@ extern bool initcall_debug;
 
 #ifndef __ASSEMBLY__
 
-#ifdef CONFIG_LTO
-/* Work around a LTO gcc problem: when there is no reference to a variable
- * in a module it will be moved to the end of the program. This causes
- * reordering of initcalls which the kernel does not like.
- * Add a dummy reference function to avoid this. The function is
- * deleted by the linker.
- */
-#define LTO_REFERENCE_INITCALL(x) \
-       ; /* yes this is needed */                      \
-       static __used __exit void *reference_##x(void)  \
-       {                                               \
-               return &x;                              \
-       }
-#else
-#define LTO_REFERENCE_INITCALL(x)
-#endif
-
-/* initcalls are now grouped by functionality into separate 
+/*
+ * initcalls are now grouped by functionality into separate
  * subsections. Ordering inside the subsections is determined
  * by link order. 
  * For backwards compatibility, initcall() puts the call in 
@@ -164,12 +149,16 @@ extern bool initcall_debug;
  *
  * The `id' arg to __define_initcall() is needed so that multiple initcalls
  * can point at the same handler without causing duplicate-symbol build errors.
+ *
+ * Initcalls are run by placing pointers in initcall sections that the
+ * kernel iterates at runtime. The linker can do dead code / data elimination
+ * and remove that completely, so the initcall sections have to be marked
+ * as KEEP() in the linker script.
  */
 
 #define __define_initcall(fn, id) \
        static initcall_t __initcall_##fn##id __used \
-       __attribute__((__section__(".initcall" #id ".init"))) = fn; \
-       LTO_REFERENCE_INITCALL(__initcall_##fn##id)
+       __attribute__((__section__(".initcall" #id ".init"))) = fn;
 
 /*
  * Early initcalls run before initializing SMP.
@@ -205,15 +194,15 @@ extern bool initcall_debug;
 
 #define __initcall(fn) device_initcall(fn)
 
-#define __exitcall(fn) \
+#define __exitcall(fn)                                         \
        static exitcall_t __exitcall_##fn __exit_call = fn
 
-#define console_initcall(fn) \
-       static initcall_t __initcall_##fn \
+#define console_initcall(fn)                                   \
+       static initcall_t __initcall_##fn                       \
        __used __section(.con_initcall.init) = fn
 
-#define security_initcall(fn) \
-       static initcall_t __initcall_##fn \
+#define security_initcall(fn)                                  \
+       static initcall_t __initcall_##fn                       \
        __used __section(.security_initcall.init) = fn
 
 struct obs_kernel_param {
index d600303306eb7b54bbc68b62c89243e778f2a75c..820c0ad54a0117596e63bae6845b1ebd771c3412 100644 (file)
@@ -44,6 +44,7 @@ static inline void kasan_disable_current(void)
 void kasan_unpoison_shadow(const void *address, size_t size);
 
 void kasan_unpoison_task_stack(struct task_struct *task);
+void kasan_unpoison_stack_above_sp_to(const void *watermark);
 
 void kasan_alloc_pages(struct page *page, unsigned int order);
 void kasan_free_pages(struct page *page, unsigned int order);
@@ -85,6 +86,7 @@ size_t kasan_metadata_size(struct kmem_cache *cache);
 static inline void kasan_unpoison_shadow(const void *address, size_t size) {}
 
 static inline void kasan_unpoison_task_stack(struct task_struct *task) {}
+static inline void kasan_unpoison_stack_above_sp_to(const void *watermark) {}
 
 static inline void kasan_enable_current(void) {}
 static inline void kasan_disable_current(void) {}
index 96356ef012de722fe4234cefe606392bfb5ca36a..7056238fd9f5cfd0b495edbbd75093f9141977e5 100644 (file)
@@ -269,10 +269,8 @@ static inline bool kernfs_ns_enabled(struct kernfs_node *kn)
 }
 
 int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen);
-size_t kernfs_path_len(struct kernfs_node *kn);
 int kernfs_path_from_node(struct kernfs_node *root_kn, struct kernfs_node *kn,
                          char *buf, size_t buflen);
-char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen);
 void pr_cont_kernfs_name(struct kernfs_node *kn);
 void pr_cont_kernfs_path(struct kernfs_node *kn);
 struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn);
@@ -341,12 +339,10 @@ static inline bool kernfs_ns_enabled(struct kernfs_node *kn)
 static inline int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
 { return -ENOSYS; }
 
-static inline size_t kernfs_path_len(struct kernfs_node *kn)
-{ return 0; }
-
-static inline char *kernfs_path(struct kernfs_node *kn, char *buf,
-                               size_t buflen)
-{ return NULL; }
+static inline int kernfs_path_from_node(struct kernfs_node *root_kn,
+                                       struct kernfs_node *kn,
+                                       char *buf, size_t buflen)
+{ return -ENOSYS; }
 
 static inline void pr_cont_kernfs_name(struct kernfs_node *kn) { }
 static inline void pr_cont_kernfs_path(struct kernfs_node *kn) { }
@@ -436,6 +432,22 @@ static inline void kernfs_init(void) { }
 
 #endif /* CONFIG_KERNFS */
 
+/**
+ * kernfs_path - build full path of a given node
+ * @kn: kernfs_node of interest
+ * @buf: buffer to copy @kn's name into
+ * @buflen: size of @buf
+ *
+ * Builds and returns the full path of @kn in @buf of @buflen bytes.  The
+ * path is built from the end of @buf so the returned pointer usually
+ * doesn't match @buf.  If @buf isn't long enough, @buf is nul terminated
+ * and %NULL is returned.
+ */
+static inline int kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen)
+{
+       return kernfs_path_from_node(kn, NULL, buf, buflen);
+}
+
 static inline struct kernfs_node *
 kernfs_find_and_get(struct kernfs_node *kn, const char *name)
 {
index e37d4f99f510ae2f1953ea07c9b6fe29a0cc401d..616eef4d81ea38af48cf809ecdbc2a721aff4640 100644 (file)
@@ -46,7 +46,8 @@
 #ifdef CONFIG_ATA_NONSTANDARD
 #include <asm/libata-portmap.h>
 #else
-#include <asm-generic/libata-portmap.h>
+#define ATA_PRIMARY_IRQ(dev)   14
+#define ATA_SECONDARY_IRQ(dev) 15
 #endif
 
 /*
index 77c141797152ef80af0a9e2d0070bfc56e796f27..58276144ba81f338ae82f8d3ab960c7dff9d58e3 100644 (file)
@@ -92,12 +92,21 @@ __mlx5_mask(typ, fld))
        ___t; \
 })
 
-#define MLX5_SET64(typ, p, fld, v) do { \
+#define __MLX5_SET64(typ, p, fld, v) do { \
        BUILD_BUG_ON(__mlx5_bit_sz(typ, fld) != 64); \
-       BUILD_BUG_ON(__mlx5_bit_off(typ, fld) % 64); \
        *((__be64 *)(p) + __mlx5_64_off(typ, fld)) = cpu_to_be64(v); \
 } while (0)
 
+#define MLX5_SET64(typ, p, fld, v) do { \
+       BUILD_BUG_ON(__mlx5_bit_off(typ, fld) % 64); \
+       __MLX5_SET64(typ, p, fld, v); \
+} while (0)
+
+#define MLX5_ARRAY_SET64(typ, p, fld, idx, v) do { \
+       BUILD_BUG_ON(__mlx5_bit_off(typ, fld) % 64); \
+       __MLX5_SET64(typ, p, fld[idx], v); \
+} while (0)
+
 #define MLX5_GET64(typ, p, fld) be64_to_cpu(*((__be64 *)(p) + __mlx5_64_off(typ, fld)))
 
 #define MLX5_GET64_PR(typ, p, fld) ({ \
index e9caec6a51e97a618ff5ab9b76dd6371708564e8..ffbd72979ee70fc3a73a2e660fd50f98b3eacc9a 100644 (file)
@@ -1266,9 +1266,10 @@ static inline int fixup_user_fault(struct task_struct *tsk,
 }
 #endif
 
-extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
+extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len,
+               unsigned int gup_flags);
 extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
-               void *buf, int len, int write);
+               void *buf, int len, unsigned int gup_flags);
 
 long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                      unsigned long start, unsigned long nr_pages,
@@ -1276,19 +1277,18 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                      struct vm_area_struct **vmas, int *nonblocking);
 long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
                            unsigned long start, unsigned long nr_pages,
-                           int write, int force, struct page **pages,
+                           unsigned int gup_flags, struct page **pages,
                            struct vm_area_struct **vmas);
 long get_user_pages(unsigned long start, unsigned long nr_pages,
-                           int write, int force, struct page **pages,
+                           unsigned int gup_flags, struct page **pages,
                            struct vm_area_struct **vmas);
 long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
-                   int write, int force, struct page **pages, int *locked);
+                   unsigned int gup_flags, struct page **pages, int *locked);
 long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
                               unsigned long start, unsigned long nr_pages,
-                              int write, int force, struct page **pages,
-                              unsigned int gup_flags);
+                              struct page **pages, unsigned int gup_flags);
 long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
-                   int write, int force, struct page **pages);
+                   struct page **pages, unsigned int gup_flags);
 int get_user_pages_fast(unsigned long start, int nr_pages, int write,
                        struct page **pages);
 
@@ -1306,7 +1306,7 @@ struct frame_vector {
 struct frame_vector *frame_vector_create(unsigned int nr_frames);
 void frame_vector_destroy(struct frame_vector *vec);
 int get_vaddr_frames(unsigned long start, unsigned int nr_pfns,
-                    bool write, bool force, struct frame_vector *vec);
+                    unsigned int gup_flags, struct frame_vector *vec);
 void put_vaddr_frames(struct frame_vector *vec);
 int frame_vector_to_pages(struct frame_vector *vec);
 void frame_vector_to_pfns(struct frame_vector *vec);
@@ -2232,6 +2232,7 @@ static inline struct page *follow_page(struct vm_area_struct *vma,
 #define FOLL_TRIED     0x800   /* a retry, previous pass started an IO */
 #define FOLL_MLOCK     0x1000  /* lock present pages */
 #define FOLL_REMOTE    0x2000  /* we are working on non-current tsk/mm */
+#define FOLL_COW       0x4000  /* internal GUP flag */
 
 typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr,
                        void *data);
index c6564ada9bebb8ea0787e60fc8cea3b46d09433c..9094faf0699d61b7b07bc5b9b0d0e9f76dc67742 100644 (file)
@@ -67,6 +67,7 @@ struct nfs4_stateid_struct {
                NFS4_DELEGATION_STATEID_TYPE,
                NFS4_LAYOUT_STATEID_TYPE,
                NFS4_PNFS_DS_STATEID_TYPE,
+               NFS4_REVOKED_STATEID_TYPE,
        } type;
 };
 
index 14a762d2734d91fff13fd17ebcf76978eb9feb2a..b34097c6784864193e67053f8fa8e5bbbc092071 100644 (file)
@@ -103,6 +103,9 @@ struct nfs_client {
 #define NFS_SP4_MACH_CRED_WRITE    5   /* WRITE */
 #define NFS_SP4_MACH_CRED_COMMIT   6   /* COMMIT */
 #define NFS_SP4_MACH_CRED_PNFS_CLEANUP  7 /* LAYOUTRETURN */
+#if IS_ENABLED(CONFIG_NFS_V4_1)
+       wait_queue_head_t       cl_lock_waitq;
+#endif /* CONFIG_NFS_V4_1 */
 #endif /* CONFIG_NFS_V4 */
 
        /* Our own IP address, as a null-terminated string.
index 7cc0deee5bdeb692a1717f74167dbfefedd5bb5c..beb1e10f446ea6365a6eb810972da2edb9c31c59 100644 (file)
@@ -124,6 +124,11 @@ struct nfs_fattr {
                | NFS_ATTR_FATTR_SPACE_USED \
                | NFS_ATTR_FATTR_V4_SECURITY_LABEL)
 
+/*
+ * Maximal number of supported layout drivers.
+ */
+#define NFS_MAX_LAYOUT_TYPES 8
+
 /*
  * Info on the file system
  */
@@ -139,7 +144,8 @@ struct nfs_fsinfo {
        __u64                   maxfilesize;
        struct timespec         time_delta; /* server time granularity */
        __u32                   lease_time; /* in seconds */
-       __u32                   layouttype; /* supported pnfs layout driver */
+       __u32                   nlayouttypes; /* number of layouttypes */
+       __u32                   layouttype[NFS_MAX_LAYOUT_TYPES]; /* supported pnfs layout driver */
        __u32                   blksize; /* preferred pnfs io block size */
        __u32                   clone_blksize; /* granularity of a CLONE operation */
 };
index e4c08c1ff0c5906786f549c36fbc9b060f323e58..a1bacf1150b2146e6cf560740459edf08f0c18a5 100644 (file)
@@ -25,7 +25,6 @@ static inline int mm_pkey_alloc(struct mm_struct *mm)
 
 static inline int mm_pkey_free(struct mm_struct *mm, int pkey)
 {
-       WARN_ONCE(1, "free of protection key when disabled");
        return -EINVAL;
 }
 
index f1bbae014889b67a219a73c1e76156aa53229024..2c6c5114c089456cf0d3bac380a922132b077b7d 100644 (file)
@@ -641,6 +641,7 @@ static inline void pwm_remove_table(struct pwm_lookup *table, size_t num)
 #ifdef CONFIG_PWM_SYSFS
 void pwmchip_sysfs_export(struct pwm_chip *chip);
 void pwmchip_sysfs_unexport(struct pwm_chip *chip);
+void pwmchip_sysfs_unexport_children(struct pwm_chip *chip);
 #else
 static inline void pwmchip_sysfs_export(struct pwm_chip *chip)
 {
@@ -649,6 +650,10 @@ static inline void pwmchip_sysfs_export(struct pwm_chip *chip)
 static inline void pwmchip_sysfs_unexport(struct pwm_chip *chip)
 {
 }
+
+static inline void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
+{
+}
 #endif /* CONFIG_PWM_SYSFS */
 
 #endif /* __LINUX_PWM_H */
index f7bb7a355cf71381100730a2379ba206bc3258ea..7bd2403e4fef1ad7fb0a5f03b4e104e96234d26b 100644 (file)
@@ -18,9 +18,20 @@ struct random_ready_callback {
 };
 
 extern void add_device_randomness(const void *, unsigned int);
+
+#if defined(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) && !defined(__CHECKER__)
+static inline void add_latent_entropy(void)
+{
+       add_device_randomness((const void *)&latent_entropy,
+                             sizeof(latent_entropy));
+}
+#else
+static inline void add_latent_entropy(void) {}
+#endif
+
 extern void add_input_randomness(unsigned int type, unsigned int code,
-                                unsigned int value);
-extern void add_interrupt_randomness(int irq, int irq_flags);
+                                unsigned int value) __latent_entropy;
+extern void add_interrupt_randomness(int irq, int irq_flags) __latent_entropy;
 
 extern void get_random_bytes(void *buf, int nbytes);
 extern int add_random_ready_callback(struct random_ready_callback *rdy);
index 4ccf184e971f3572a1fe5aa105535e17d75e7989..b1bc62ba20a26d56df1d43794023a60e80e1ffef 100644 (file)
@@ -131,6 +131,7 @@ struct rpc_authops {
        struct rpc_auth *       (*create)(struct rpc_auth_create_args *, struct rpc_clnt *);
        void                    (*destroy)(struct rpc_auth *);
 
+       int                     (*hash_cred)(struct auth_cred *, unsigned int);
        struct rpc_cred *       (*lookup_cred)(struct rpc_auth *, struct auth_cred *, int);
        struct rpc_cred *       (*crcreate)(struct rpc_auth*, struct auth_cred *, int, gfp_t);
        int                     (*list_pseudoflavors)(rpc_authflavor_t *, int);
index 5c02b0691587797e303758eb74a4cad7d5e577ff..85cc819676e81d9f0e85331a17f70b271cd680d1 100644 (file)
@@ -125,6 +125,13 @@ struct rpc_create_args {
        struct svc_xprt         *bc_xprt;       /* NFSv4.1 backchannel */
 };
 
+struct rpc_add_xprt_test {
+       int (*add_xprt_test)(struct rpc_clnt *,
+               struct rpc_xprt *,
+               void *calldata);
+       void *data;
+};
+
 /* Values for "flags" field */
 #define RPC_CLNT_CREATE_HARDRTRY       (1UL << 0)
 #define RPC_CLNT_CREATE_AUTOBIND       (1UL << 2)
@@ -198,6 +205,16 @@ int                rpc_clnt_add_xprt(struct rpc_clnt *, struct xprt_create *,
 void           rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt,
                        unsigned long timeo);
 
+int            rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *,
+                       struct rpc_xprt_switch *,
+                       struct rpc_xprt *,
+                       void *);
+
 const char *rpc_proc_name(const struct rpc_task *task);
+
+void rpc_clnt_xprt_switch_put(struct rpc_clnt *);
+void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *, struct rpc_xprt *);
+bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
+                       const struct sockaddr *sap);
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SUNRPC_CLNT_H */
index 3b1ff38f0c37aac2aff8a536b8a49da4b8433207..cfda6adcf33cfcf3c28e46066ec294c6d2902389 100644 (file)
 #define _LINUX_SUNRPC_RPC_RDMA_H
 
 #include <linux/types.h>
+#include <linux/bitops.h>
 
 #define RPCRDMA_VERSION                1
 #define rpcrdma_version                cpu_to_be32(RPCRDMA_VERSION)
 
+enum {
+       RPCRDMA_V1_DEF_INLINE_SIZE      = 1024,
+};
+
 struct rpcrdma_segment {
        __be32 rs_handle;       /* Registered memory handle */
        __be32 rs_length;       /* Length of the chunk in bytes */
@@ -129,4 +134,38 @@ enum rpcrdma_proc {
 #define rdma_done      cpu_to_be32(RDMA_DONE)
 #define rdma_error     cpu_to_be32(RDMA_ERROR)
 
+/*
+ * Private extension to RPC-over-RDMA Version One.
+ * Message passed during RDMA-CM connection set-up.
+ *
+ * Add new fields at the end, and don't permute existing
+ * fields.
+ */
+struct rpcrdma_connect_private {
+       __be32                  cp_magic;
+       u8                      cp_version;
+       u8                      cp_flags;
+       u8                      cp_send_size;
+       u8                      cp_recv_size;
+} __packed;
+
+#define rpcrdma_cmp_magic      __cpu_to_be32(0xf6ab0e18)
+
+enum {
+       RPCRDMA_CMP_VERSION             = 1,
+       RPCRDMA_CMP_F_SND_W_INV_OK      = BIT(0),
+};
+
+static inline u8
+rpcrdma_encode_buffer_size(unsigned int size)
+{
+       return (size >> 10) - 1;
+}
+
+static inline unsigned int
+rpcrdma_decode_buffer_size(u8 val)
+{
+       return ((unsigned int)val + 1) << 10;
+}
+
 #endif                         /* _LINUX_SUNRPC_RPC_RDMA_H */
index 817af0b4385ea384026b7f35e296e94211796974..7ba040c797ec41ea41d46098bec89fba30daf603 100644 (file)
@@ -239,8 +239,8 @@ struct rpc_task *rpc_wake_up_first(struct rpc_wait_queue *,
                                        void *);
 void           rpc_wake_up_status(struct rpc_wait_queue *, int);
 void           rpc_delay(struct rpc_task *, unsigned long);
-void *         rpc_malloc(struct rpc_task *, size_t);
-void           rpc_free(void *);
+int            rpc_malloc(struct rpc_task *);
+void           rpc_free(struct rpc_task *);
 int            rpciod_up(void);
 void           rpciod_down(void);
 int            __rpc_wait_for_completion_task(struct rpc_task *task, wait_bit_action_f *);
index d6917b896d3a75bfb4f26652903d710471a4726b..cc3ae16eac6838a9f904e88463f935a633b728b7 100644 (file)
@@ -86,6 +86,7 @@ struct svc_rdma_op_ctxt {
        unsigned long flags;
        enum dma_data_direction direction;
        int count;
+       unsigned int mapped_sges;
        struct ib_sge sge[RPCSVC_MAXPAGES];
        struct page *pages[RPCSVC_MAXPAGES];
 };
@@ -136,6 +137,7 @@ struct svcxprt_rdma {
        int                  sc_ord;            /* RDMA read limit */
        int                  sc_max_sge;
        int                  sc_max_sge_rd;     /* max sge for read target */
+       bool                 sc_snd_w_inv;      /* OK to use Send With Invalidate */
 
        atomic_t             sc_sq_count;       /* Number of SQ WR on queue */
        unsigned int         sc_sq_depth;       /* Depth of SQ */
@@ -193,6 +195,14 @@ struct svcxprt_rdma {
 
 #define RPCSVC_MAXPAYLOAD_RDMA RPCSVC_MAXPAYLOAD
 
+/* Track DMA maps for this transport and context */
+static inline void svc_rdma_count_mappings(struct svcxprt_rdma *rdma,
+                                          struct svc_rdma_op_ctxt *ctxt)
+{
+       ctxt->mapped_sges++;
+       atomic_inc(&rdma->sc_dma_used);
+}
+
 /* svc_rdma_backchannel.c */
 extern int svc_rdma_handle_bc_reply(struct rpc_xprt *xprt,
                                    struct rpcrdma_msg *rmsgp,
index 70c6b92e15a7ca74e7246cacf4768a6b95eccb4f..56c48c884a242daadbed87dcea6ab1121884b742 100644 (file)
@@ -67,6 +67,18 @@ struct xdr_buf {
                        len;            /* Length of XDR encoded message */
 };
 
+static inline void
+xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
+{
+       buf->head[0].iov_base = start;
+       buf->head[0].iov_len = len;
+       buf->tail[0].iov_len = 0;
+       buf->page_len = 0;
+       buf->flags = 0;
+       buf->len = 0;
+       buf->buflen = len;
+}
+
 /*
  * pre-xdr'ed macros.
  */
index a16070dd03eefe9281476183ff4b5a0692523a09..a5da60b24d83ec79fb75bae8cd91ae52b6bcccc8 100644 (file)
@@ -83,9 +83,11 @@ struct rpc_rqst {
        void (*rq_release_snd_buf)(struct rpc_rqst *); /* release rq_enc_pages */
        struct list_head        rq_list;
 
-       __u32 *                 rq_buffer;      /* XDR encode buffer */
-       size_t                  rq_callsize,
-                               rq_rcvsize;
+       void                    *rq_xprtdata;   /* Per-xprt private data */
+       void                    *rq_buffer;     /* Call XDR encode buffer */
+       size_t                  rq_callsize;
+       void                    *rq_rbuffer;    /* Reply XDR decode buffer */
+       size_t                  rq_rcvsize;
        size_t                  rq_xmit_bytes_sent;     /* total bytes sent */
        size_t                  rq_reply_bytes_recvd;   /* total reply bytes */
                                                        /* received */
@@ -127,8 +129,8 @@ struct rpc_xprt_ops {
        void            (*rpcbind)(struct rpc_task *task);
        void            (*set_port)(struct rpc_xprt *xprt, unsigned short port);
        void            (*connect)(struct rpc_xprt *xprt, struct rpc_task *task);
-       void *          (*buf_alloc)(struct rpc_task *task, size_t size);
-       void            (*buf_free)(void *buffer);
+       int             (*buf_alloc)(struct rpc_task *task);
+       void            (*buf_free)(struct rpc_task *task);
        int             (*send_request)(struct rpc_task *task);
        void            (*set_retrans_timeout)(struct rpc_task *task);
        void            (*timer)(struct rpc_xprt *xprt, struct rpc_task *task);
index 5a9acffa41beb046945932005d18c7d70b7fb95d..507418c1c69e984e7890ae398c0b0c0e249aad9d 100644 (file)
@@ -66,4 +66,6 @@ extern struct rpc_xprt *xprt_iter_xprt(struct rpc_xprt_iter *xpi);
 extern struct rpc_xprt *xprt_iter_get_xprt(struct rpc_xprt_iter *xpi);
 extern struct rpc_xprt *xprt_iter_get_next(struct rpc_xprt_iter *xpi);
 
+extern bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,
+               const struct sockaddr *sap);
 #endif
index 39267dc3486af5417f1f0642d353ec65d1e78aa5..221b7a2e5406e2e3ba72e5ba8dac3c17c11bbfea 100644 (file)
@@ -53,8 +53,8 @@
 #define RPCRDMA_MAX_SLOT_TABLE (256U)
 
 #define RPCRDMA_MIN_INLINE  (1024)     /* min inline thresh */
-#define RPCRDMA_DEF_INLINE  (1024)     /* default inline thresh */
-#define RPCRDMA_MAX_INLINE  (3068)     /* max inline thresh */
+#define RPCRDMA_DEF_INLINE  (4096)     /* default inline thresh */
+#define RPCRDMA_MAX_INLINE  (65536)    /* max inline thresh */
 
 /* Memory registration strategies, by number.
  * This is part of a kernel / user space API. Do not remove. */
index 0d7abb8b7315ce3ab5162bc606c64337f3709d20..91a740f6b884236e3ed5771f01397f4647f3cd9d 100644 (file)
@@ -902,8 +902,5 @@ asmlinkage long sys_pkey_mprotect(unsigned long start, size_t len,
                                  unsigned long prot, int pkey);
 asmlinkage long sys_pkey_alloc(unsigned long flags, unsigned long init_val);
 asmlinkage long sys_pkey_free(int pkey);
-//asmlinkage long sys_pkey_get(int pkey, unsigned long flags);
-//asmlinkage long sys_pkey_set(int pkey, unsigned long access_rights,
-//                          unsigned long flags);
 
 #endif
index ee517bef0db04902412c17b7eceac24839a4564f..511182a88e76416ded9f506f1af2db76bddf2e32 100644 (file)
@@ -92,12 +92,24 @@ enum thermal_trend {
        THERMAL_TREND_DROP_FULL, /* apply lowest cooling action */
 };
 
+/* Thermal notification reason */
+enum thermal_notify_event {
+       THERMAL_EVENT_UNSPECIFIED, /* Unspecified event */
+       THERMAL_EVENT_TEMP_SAMPLE, /* New Temperature sample */
+       THERMAL_TRIP_VIOLATED, /* TRIP Point violation */
+       THERMAL_TRIP_CHANGED, /* TRIP Point temperature changed */
+       THERMAL_DEVICE_DOWN, /* Thermal device is down */
+       THERMAL_DEVICE_UP, /* Thermal device is up after a down event */
+       THERMAL_DEVICE_POWER_CAPABILITY_CHANGED, /* power capability changed */
+};
+
 struct thermal_zone_device_ops {
        int (*bind) (struct thermal_zone_device *,
                     struct thermal_cooling_device *);
        int (*unbind) (struct thermal_zone_device *,
                       struct thermal_cooling_device *);
        int (*get_temp) (struct thermal_zone_device *, int *);
+       int (*set_trips) (struct thermal_zone_device *, int, int);
        int (*get_mode) (struct thermal_zone_device *,
                         enum thermal_device_mode *);
        int (*set_mode) (struct thermal_zone_device *,
@@ -168,6 +180,10 @@ struct thermal_attr {
  * @last_temperature:  previous temperature read
  * @emul_temperature:  emulated temperature when using CONFIG_THERMAL_EMULATION
  * @passive:           1 if you've crossed a passive trip point, 0 otherwise.
+ * @prev_low_trip:     the low current temperature if you've crossed a passive
+                       trip point.
+ * @prev_high_trip:    the above current temperature if you've crossed a
+                       passive trip point.
  * @forced_passive:    If > 0, temperature at which to switch on all ACPI
  *                     processor cooling devices.  Currently only used by the
  *                     step-wise governor.
@@ -182,6 +198,7 @@ struct thermal_attr {
  * @lock:      lock to protect thermal_instances list
  * @node:      node in thermal_tz_list (in thermal_core.c)
  * @poll_queue:        delayed work for polling
+ * @notify_event: Last notification event
  */
 struct thermal_zone_device {
        int id;
@@ -199,6 +216,8 @@ struct thermal_zone_device {
        int last_temperature;
        int emul_temperature;
        int passive;
+       int prev_low_trip;
+       int prev_high_trip;
        unsigned int forced_passive;
        atomic_t need_update;
        struct thermal_zone_device_ops *ops;
@@ -210,6 +229,7 @@ struct thermal_zone_device {
        struct mutex lock;
        struct list_head node;
        struct delayed_work poll_queue;
+       enum thermal_notify_event notify_event;
 };
 
 /**
@@ -333,6 +353,9 @@ struct thermal_genl_event {
  *
  * Optional:
  * @get_trend: a pointer to a function that reads the sensor temperature trend.
+ * @set_trips: a pointer to a function that sets a temperature window. When
+ *            this window is left the driver must inform the thermal core via
+ *            thermal_zone_device_update.
  * @set_emul_temp: a pointer to a function that sets sensor emulated
  *                temperature.
  * @set_trip_temp: a pointer to a function that sets the trip temperature on
@@ -340,7 +363,8 @@ struct thermal_genl_event {
  */
 struct thermal_zone_of_device_ops {
        int (*get_temp)(void *, int *);
-       int (*get_trend)(void *, long *);
+       int (*get_trend)(void *, int, enum thermal_trend *);
+       int (*set_trips)(void *, int, int);
        int (*set_emul_temp)(void *, int);
        int (*set_trip_temp)(void *, int, int);
 };
@@ -425,7 +449,9 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
                                     unsigned int);
 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
                                       struct thermal_cooling_device *);
-void thermal_zone_device_update(struct thermal_zone_device *);
+void thermal_zone_device_update(struct thermal_zone_device *,
+                               enum thermal_notify_event);
+void thermal_zone_set_trips(struct thermal_zone_device *);
 
 struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
                const struct thermal_cooling_device_ops *);
@@ -435,6 +461,8 @@ thermal_of_cooling_device_register(struct device_node *np, char *, void *,
 void thermal_cooling_device_unregister(struct thermal_cooling_device *);
 struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
 int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
+int thermal_zone_get_slope(struct thermal_zone_device *tz);
+int thermal_zone_get_offset(struct thermal_zone_device *tz);
 
 int get_tz_trend(struct thermal_zone_device *, int);
 struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
@@ -473,7 +501,10 @@ static inline int thermal_zone_unbind_cooling_device(
        struct thermal_zone_device *tz, int trip,
        struct thermal_cooling_device *cdev)
 { return -ENODEV; }
-static inline void thermal_zone_device_update(struct thermal_zone_device *tz)
+static inline void thermal_zone_device_update(struct thermal_zone_device *tz,
+                                             enum thermal_notify_event event)
+{ }
+static inline void thermal_zone_set_trips(struct thermal_zone_device *tz)
 { }
 static inline struct thermal_cooling_device *
 thermal_cooling_device_register(char *type, void *devdata,
@@ -492,6 +523,12 @@ static inline struct thermal_zone_device *thermal_zone_get_zone_by_name(
 static inline int thermal_zone_get_temp(
                struct thermal_zone_device *tz, int *temp)
 { return -ENODEV; }
+static inline int thermal_zone_get_slope(
+               struct thermal_zone_device *tz)
+{ return -ENODEV; }
+static inline int thermal_zone_get_offset(
+               struct thermal_zone_device *tz)
+{ return -ENODEV; }
 static inline int get_tz_trend(struct thermal_zone_device *tz, int trip)
 { return -ENODEV; }
 static inline struct thermal_instance *
index 7047bc7f810688bff4511a2d087d5207676b62b3..35a4d8185b51cd83492743c043aea3ffa1eca9f9 100644 (file)
@@ -19,6 +19,7 @@
 struct watchdog_ops;
 struct watchdog_device;
 struct watchdog_core_data;
+struct watchdog_governor;
 
 /** struct watchdog_ops - The watchdog-devices operations
  *
@@ -28,6 +29,7 @@ struct watchdog_core_data;
  * @ping:      The routine that sends a keepalive ping to the watchdog device.
  * @status:    The routine that shows the status of the watchdog device.
  * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds).
+ * @set_pretimeout:The routine for setting the watchdog devices pretimeout.
  * @get_timeleft:The routine that gets the time left before a reset (in seconds).
  * @restart:   The routine for restarting the machine.
  * @ioctl:     The routines that handles extra ioctl calls.
@@ -46,6 +48,7 @@ struct watchdog_ops {
        int (*ping)(struct watchdog_device *);
        unsigned int (*status)(struct watchdog_device *);
        int (*set_timeout)(struct watchdog_device *, unsigned int);
+       int (*set_pretimeout)(struct watchdog_device *, unsigned int);
        unsigned int (*get_timeleft)(struct watchdog_device *);
        int (*restart)(struct watchdog_device *, unsigned long, void *);
        long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
@@ -59,8 +62,10 @@ struct watchdog_ops {
  *             watchdog device.
  * @info:      Pointer to a watchdog_info structure.
  * @ops:       Pointer to the list of watchdog operations.
+ * @gov:       Pointer to watchdog pretimeout governor.
  * @bootstatus:        Status of the watchdog device at boot.
  * @timeout:   The watchdog devices timeout value (in seconds).
+ * @pretimeout: The watchdog devices pre_timeout value.
  * @min_timeout:The watchdog devices minimum timeout value (in seconds).
  * @max_timeout:The watchdog devices maximum timeout value (in seconds)
  *             as configurable from user space. Only relevant if
@@ -94,8 +99,10 @@ struct watchdog_device {
        const struct attribute_group **groups;
        const struct watchdog_info *info;
        const struct watchdog_ops *ops;
+       const struct watchdog_governor *gov;
        unsigned int bootstatus;
        unsigned int timeout;
+       unsigned int pretimeout;
        unsigned int min_timeout;
        unsigned int max_timeout;
        unsigned int min_hw_heartbeat_ms;
@@ -163,6 +170,13 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
                 t > wdd->max_timeout);
 }
 
+/* Use the following function to check if a pretimeout value is invalid */
+static inline bool watchdog_pretimeout_invalid(struct watchdog_device *wdd,
+                                              unsigned int t)
+{
+       return t && wdd->timeout && t >= wdd->timeout;
+}
+
 /* Use the following functions to manipulate watchdog driver specific data */
 static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
 {
@@ -174,6 +188,16 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
        return wdd->driver_data;
 }
 
+/* Use the following functions to report watchdog pretimeout event */
+#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)
+void watchdog_notify_pretimeout(struct watchdog_device *wdd);
+#else
+static inline void watchdog_notify_pretimeout(struct watchdog_device *wdd)
+{
+       pr_alert("watchdog%d: pretimeout event\n", wdd->id);
+}
+#endif
+
 /* drivers/watchdog/watchdog_core.c */
 void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
 extern int watchdog_init_timeout(struct watchdog_device *wdd,
index fe78f02a242e723e29dfdfb4baf39bbadc68962d..bd19faad0d9620026f7bf923a53ed9c4e4094f7d 100644 (file)
@@ -796,9 +796,9 @@ enum station_parameters_apply_mask {
  *     (or NULL for no change)
  * @supported_rates_len: number of supported rates
  * @sta_flags_mask: station flags that changed
- *     (bitmask of BIT(NL80211_STA_FLAG_...))
+ *     (bitmask of BIT(%NL80211_STA_FLAG_...))
  * @sta_flags_set: station flags values
- *     (bitmask of BIT(NL80211_STA_FLAG_...))
+ *     (bitmask of BIT(%NL80211_STA_FLAG_...))
  * @listen_interval: listen interval or -1 for no change
  * @aid: AID or zero for no change
  * @peer_aid: mesh peer AID or zero for no change
@@ -3088,47 +3088,54 @@ struct ieee80211_iface_limit {
  *
  * 1. Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total:
  *
- *  struct ieee80211_iface_limit limits1[] = {
- *     { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
- *     { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, },
- *  };
- *  struct ieee80211_iface_combination combination1 = {
- *     .limits = limits1,
- *     .n_limits = ARRAY_SIZE(limits1),
- *     .max_interfaces = 2,
- *     .beacon_int_infra_match = true,
- *  };
+ *    .. code-block:: c
+ *
+ *     struct ieee80211_iface_limit limits1[] = {
+ *             { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
+ *             { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, },
+ *     };
+ *     struct ieee80211_iface_combination combination1 = {
+ *             .limits = limits1,
+ *             .n_limits = ARRAY_SIZE(limits1),
+ *             .max_interfaces = 2,
+ *             .beacon_int_infra_match = true,
+ *     };
  *
  *
  * 2. Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total:
  *
- *  struct ieee80211_iface_limit limits2[] = {
- *     { .max = 8, .types = BIT(NL80211_IFTYPE_AP) |
- *                          BIT(NL80211_IFTYPE_P2P_GO), },
- *  };
- *  struct ieee80211_iface_combination combination2 = {
- *     .limits = limits2,
- *     .n_limits = ARRAY_SIZE(limits2),
- *     .max_interfaces = 8,
- *     .num_different_channels = 1,
- *  };
+ *    .. code-block:: c
+ *
+ *     struct ieee80211_iface_limit limits2[] = {
+ *             { .max = 8, .types = BIT(NL80211_IFTYPE_AP) |
+ *                                  BIT(NL80211_IFTYPE_P2P_GO), },
+ *     };
+ *     struct ieee80211_iface_combination combination2 = {
+ *             .limits = limits2,
+ *             .n_limits = ARRAY_SIZE(limits2),
+ *             .max_interfaces = 8,
+ *             .num_different_channels = 1,
+ *     };
  *
  *
  * 3. Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total.
  *
- * This allows for an infrastructure connection and three P2P connections.
- *
- *  struct ieee80211_iface_limit limits3[] = {
- *     { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
- *     { .max = 3, .types = BIT(NL80211_IFTYPE_P2P_GO) |
- *                          BIT(NL80211_IFTYPE_P2P_CLIENT), },
- *  };
- *  struct ieee80211_iface_combination combination3 = {
- *     .limits = limits3,
- *     .n_limits = ARRAY_SIZE(limits3),
- *     .max_interfaces = 4,
- *     .num_different_channels = 2,
- *  };
+ *    This allows for an infrastructure connection and three P2P connections.
+ *
+ *    .. code-block:: c
+ *
+ *     struct ieee80211_iface_limit limits3[] = {
+ *             { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
+ *             { .max = 3, .types = BIT(NL80211_IFTYPE_P2P_GO) |
+ *                                  BIT(NL80211_IFTYPE_P2P_CLIENT), },
+ *     };
+ *     struct ieee80211_iface_combination combination3 = {
+ *             .limits = limits3,
+ *             .n_limits = ARRAY_SIZE(limits3),
+ *             .max_interfaces = 4,
+ *             .num_different_channels = 2,
+ *     };
+ *
  */
 struct ieee80211_iface_combination {
        const struct ieee80211_iface_limit *limits;
index b220dabeab458a9f1ff4c00d291db3239c2b870d..3832099289c5aa607ed89bc6eb711a8e412980f0 100644 (file)
@@ -114,6 +114,25 @@ static inline u32 l3mdev_fib_table(const struct net_device *dev)
        return tb_id;
 }
 
+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;
+}
+
 struct dst_entry *l3mdev_link_scope_lookup(struct net *net, struct flowi6 *fl6);
 
 static inline
@@ -207,6 +226,11 @@ static inline u32 l3mdev_fib_table_by_index(struct net *net, int ifindex)
        return 0;
 }
 
+static inline bool netif_index_is_l3_master(struct net *net, int ifindex)
+{
+       return false;
+}
+
 static inline
 struct dst_entry *l3mdev_link_scope_lookup(struct net *net, struct flowi6 *fl6)
 {
diff --git a/include/soc/fsl/bman.h b/include/soc/fsl/bman.h
new file mode 100644 (file)
index 0000000..eaaf56d
--- /dev/null
@@ -0,0 +1,129 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __FSL_BMAN_H
+#define __FSL_BMAN_H
+
+/* wrapper for 48-bit buffers */
+struct bm_buffer {
+       union {
+               struct {
+                       __be16 bpid; /* hi 8-bits reserved */
+                       __be16 hi; /* High 16-bits of 48-bit address */
+                       __be32 lo; /* Low 32-bits of 48-bit address */
+               };
+               __be64 data;
+       };
+} __aligned(8);
+/*
+ * Restore the 48 bit address previously stored in BMan
+ * hardware pools as a dma_addr_t
+ */
+static inline dma_addr_t bm_buf_addr(const struct bm_buffer *buf)
+{
+       return be64_to_cpu(buf->data) & 0xffffffffffffLLU;
+}
+
+static inline u64 bm_buffer_get64(const struct bm_buffer *buf)
+{
+       return be64_to_cpu(buf->data) & 0xffffffffffffLLU;
+}
+
+static inline void bm_buffer_set64(struct bm_buffer *buf, u64 addr)
+{
+       buf->hi = cpu_to_be16(upper_32_bits(addr));
+       buf->lo = cpu_to_be32(lower_32_bits(addr));
+}
+
+static inline u8 bm_buffer_get_bpid(const struct bm_buffer *buf)
+{
+       return be16_to_cpu(buf->bpid) & 0xff;
+}
+
+static inline void bm_buffer_set_bpid(struct bm_buffer *buf, int bpid)
+{
+       buf->bpid = cpu_to_be16(bpid & 0xff);
+}
+
+/* Managed portal, high-level i/face */
+
+/* Portal and Buffer Pools */
+struct bman_portal;
+struct bman_pool;
+
+#define BM_POOL_MAX            64 /* max # of buffer pools */
+
+/**
+ * bman_new_pool - Allocates a Buffer Pool object
+ *
+ * Creates a pool object, and returns a reference to it or NULL on error.
+ */
+struct bman_pool *bman_new_pool(void);
+
+/**
+ * bman_free_pool - Deallocates a Buffer Pool object
+ * @pool: the pool object to release
+ */
+void bman_free_pool(struct bman_pool *pool);
+
+/**
+ * bman_get_bpid - Returns a pool object's BPID.
+ * @pool: the pool object
+ *
+ * The returned value is the index of the encapsulated buffer pool,
+ * in the range of [0, @BM_POOL_MAX-1].
+ */
+int bman_get_bpid(const struct bman_pool *pool);
+
+/**
+ * bman_release - Release buffer(s) to the buffer pool
+ * @pool: the buffer pool object to release to
+ * @bufs: an array of buffers to release
+ * @num: the number of buffers in @bufs (1-8)
+ *
+ * Adds the given buffers to RCR entries. If the RCR ring is unresponsive,
+ * the function will return -ETIMEDOUT. Otherwise, it returns zero.
+ */
+int bman_release(struct bman_pool *pool, const struct bm_buffer *bufs, u8 num);
+
+/**
+ * bman_acquire - Acquire buffer(s) from a buffer pool
+ * @pool: the buffer pool object to acquire from
+ * @bufs: array for storing the acquired buffers
+ * @num: the number of buffers desired (@bufs is at least this big)
+ *
+ * Issues an "Acquire" command via the portal's management command interface.
+ * The return value will be the number of buffers obtained from the pool, or a
+ * negative error code if a h/w error or pool starvation was encountered. In
+ * the latter case, the content of @bufs is undefined.
+ */
+int bman_acquire(struct bman_pool *pool, struct bm_buffer *bufs, u8 num);
+
+#endif /* __FSL_BMAN_H */
diff --git a/include/soc/fsl/qman.h b/include/soc/fsl/qman.h
new file mode 100644 (file)
index 0000000..37f3eb0
--- /dev/null
@@ -0,0 +1,1074 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * 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 Freescale Semiconductor 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __FSL_QMAN_H
+#define __FSL_QMAN_H
+
+#include <linux/bitops.h>
+
+/* Hardware constants */
+#define QM_CHANNEL_SWPORTAL0 0
+#define QMAN_CHANNEL_POOL1 0x21
+#define QMAN_CHANNEL_POOL1_REV3 0x401
+extern u16 qm_channel_pool1;
+
+/* Portal processing (interrupt) sources */
+#define QM_PIRQ_CSCI   0x00100000      /* Congestion State Change */
+#define QM_PIRQ_EQCI   0x00080000      /* Enqueue Command Committed */
+#define QM_PIRQ_EQRI   0x00040000      /* EQCR Ring (below threshold) */
+#define QM_PIRQ_DQRI   0x00020000      /* DQRR Ring (non-empty) */
+#define QM_PIRQ_MRI    0x00010000      /* MR Ring (non-empty) */
+/*
+ * This mask contains all the interrupt sources that need handling except DQRI,
+ * ie. that if present should trigger slow-path processing.
+ */
+#define QM_PIRQ_SLOW   (QM_PIRQ_CSCI | QM_PIRQ_EQCI | QM_PIRQ_EQRI | \
+                        QM_PIRQ_MRI)
+
+/* For qman_static_dequeue_*** APIs */
+#define QM_SDQCR_CHANNELS_POOL_MASK    0x00007fff
+/* for n in [1,15] */
+#define QM_SDQCR_CHANNELS_POOL(n)      (0x00008000 >> (n))
+/* for conversion from n of qm_channel */
+static inline u32 QM_SDQCR_CHANNELS_POOL_CONV(u16 channel)
+{
+       return QM_SDQCR_CHANNELS_POOL(channel + 1 - qm_channel_pool1);
+}
+
+/* --- QMan data structures (and associated constants) --- */
+
+/* "Frame Descriptor (FD)" */
+struct qm_fd {
+       union {
+               struct {
+                       u8 cfg8b_w1;
+                       u8 bpid;        /* Buffer Pool ID */
+                       u8 cfg8b_w3;
+                       u8 addr_hi;     /* high 8-bits of 40-bit address */
+                       __be32 addr_lo; /* low 32-bits of 40-bit address */
+               } __packed;
+               __be64 data;
+       };
+       __be32 cfg;     /* format, offset, length / congestion */
+       union {
+               __be32 cmd;
+               __be32 status;
+       };
+} __aligned(8);
+
+#define QM_FD_FORMAT_SG                BIT(31)
+#define QM_FD_FORMAT_LONG      BIT(30)
+#define QM_FD_FORMAT_COMPOUND  BIT(29)
+#define QM_FD_FORMAT_MASK      GENMASK(31, 29)
+#define QM_FD_OFF_SHIFT                20
+#define QM_FD_OFF_MASK         GENMASK(28, 20)
+#define QM_FD_LEN_MASK         GENMASK(19, 0)
+#define QM_FD_LEN_BIG_MASK     GENMASK(28, 0)
+
+enum qm_fd_format {
+       /*
+        * 'contig' implies a contiguous buffer, whereas 'sg' implies a
+        * scatter-gather table. 'big' implies a 29-bit length with no offset
+        * field, otherwise length is 20-bit and offset is 9-bit. 'compound'
+        * implies a s/g-like table, where each entry itself represents a frame
+        * (contiguous or scatter-gather) and the 29-bit "length" is
+        * interpreted purely for congestion calculations, ie. a "congestion
+        * weight".
+        */
+       qm_fd_contig = 0,
+       qm_fd_contig_big = QM_FD_FORMAT_LONG,
+       qm_fd_sg = QM_FD_FORMAT_SG,
+       qm_fd_sg_big = QM_FD_FORMAT_SG | QM_FD_FORMAT_LONG,
+       qm_fd_compound = QM_FD_FORMAT_COMPOUND
+};
+
+static inline dma_addr_t qm_fd_addr(const struct qm_fd *fd)
+{
+       return be64_to_cpu(fd->data) & 0xffffffffffLLU;
+}
+
+static inline u64 qm_fd_addr_get64(const struct qm_fd *fd)
+{
+       return be64_to_cpu(fd->data) & 0xffffffffffLLU;
+}
+
+static inline void qm_fd_addr_set64(struct qm_fd *fd, u64 addr)
+{
+       fd->addr_hi = upper_32_bits(addr);
+       fd->addr_lo = cpu_to_be32(lower_32_bits(addr));
+}
+
+/*
+ * The 'format' field indicates the interpretation of the remaining
+ * 29 bits of the 32-bit word.
+ * If 'format' is _contig or _sg, 20b length and 9b offset.
+ * If 'format' is _contig_big or _sg_big, 29b length.
+ * If 'format' is _compound, 29b "congestion weight".
+ */
+static inline enum qm_fd_format qm_fd_get_format(const struct qm_fd *fd)
+{
+       return be32_to_cpu(fd->cfg) & QM_FD_FORMAT_MASK;
+}
+
+static inline int qm_fd_get_offset(const struct qm_fd *fd)
+{
+       return (be32_to_cpu(fd->cfg) & QM_FD_OFF_MASK) >> QM_FD_OFF_SHIFT;
+}
+
+static inline int qm_fd_get_length(const struct qm_fd *fd)
+{
+       return be32_to_cpu(fd->cfg) & QM_FD_LEN_MASK;
+}
+
+static inline int qm_fd_get_len_big(const struct qm_fd *fd)
+{
+       return be32_to_cpu(fd->cfg) & QM_FD_LEN_BIG_MASK;
+}
+
+static inline void qm_fd_set_param(struct qm_fd *fd, enum qm_fd_format fmt,
+                                  int off, int len)
+{
+       fd->cfg = cpu_to_be32(fmt | (len & QM_FD_LEN_BIG_MASK) |
+                             ((off << QM_FD_OFF_SHIFT) & QM_FD_OFF_MASK));
+}
+
+#define qm_fd_set_contig(fd, off, len) \
+       qm_fd_set_param(fd, qm_fd_contig, off, len)
+#define qm_fd_set_sg(fd, off, len) qm_fd_set_param(fd, qm_fd_sg, off, len)
+#define qm_fd_set_contig_big(fd, len) \
+       qm_fd_set_param(fd, qm_fd_contig_big, 0, len)
+#define qm_fd_set_sg_big(fd, len) qm_fd_set_param(fd, qm_fd_sg_big, 0, len)
+
+static inline void qm_fd_clear_fd(struct qm_fd *fd)
+{
+       fd->data = 0;
+       fd->cfg = 0;
+       fd->cmd = 0;
+}
+
+/* Scatter/Gather table entry */
+struct qm_sg_entry {
+       union {
+               struct {
+                       u8 __reserved1[3];
+                       u8 addr_hi;     /* high 8-bits of 40-bit address */
+                       __be32 addr_lo; /* low 32-bits of 40-bit address */
+               };
+               __be64 data;
+       };
+       __be32 cfg;     /* E bit, F bit, length */
+       u8 __reserved2;
+       u8 bpid;
+       __be16 offset; /* 13-bit, _res[13-15]*/
+} __packed;
+
+#define QM_SG_LEN_MASK GENMASK(29, 0)
+#define QM_SG_OFF_MASK GENMASK(12, 0)
+#define QM_SG_FIN      BIT(30)
+#define QM_SG_EXT      BIT(31)
+
+static inline dma_addr_t qm_sg_addr(const struct qm_sg_entry *sg)
+{
+       return be64_to_cpu(sg->data) & 0xffffffffffLLU;
+}
+
+static inline u64 qm_sg_entry_get64(const struct qm_sg_entry *sg)
+{
+       return be64_to_cpu(sg->data) & 0xffffffffffLLU;
+}
+
+static inline void qm_sg_entry_set64(struct qm_sg_entry *sg, u64 addr)
+{
+       sg->addr_hi = upper_32_bits(addr);
+       sg->addr_lo = cpu_to_be32(lower_32_bits(addr));
+}
+
+static inline bool qm_sg_entry_is_final(const struct qm_sg_entry *sg)
+{
+       return be32_to_cpu(sg->cfg) & QM_SG_FIN;
+}
+
+static inline bool qm_sg_entry_is_ext(const struct qm_sg_entry *sg)
+{
+       return be32_to_cpu(sg->cfg) & QM_SG_EXT;
+}
+
+static inline int qm_sg_entry_get_len(const struct qm_sg_entry *sg)
+{
+       return be32_to_cpu(sg->cfg) & QM_SG_LEN_MASK;
+}
+
+static inline void qm_sg_entry_set_len(struct qm_sg_entry *sg, int len)
+{
+       sg->cfg = cpu_to_be32(len & QM_SG_LEN_MASK);
+}
+
+static inline void qm_sg_entry_set_f(struct qm_sg_entry *sg, int len)
+{
+       sg->cfg = cpu_to_be32(QM_SG_FIN | (len & QM_SG_LEN_MASK));
+}
+
+static inline int qm_sg_entry_get_off(const struct qm_sg_entry *sg)
+{
+       return be32_to_cpu(sg->offset) & QM_SG_OFF_MASK;
+}
+
+/* "Frame Dequeue Response" */
+struct qm_dqrr_entry {
+       u8 verb;
+       u8 stat;
+       u16 seqnum;     /* 15-bit */
+       u8 tok;
+       u8 __reserved2[3];
+       u32 fqid;       /* 24-bit */
+       u32 contextB;
+       struct qm_fd fd;
+       u8 __reserved4[32];
+} __packed;
+#define QM_DQRR_VERB_VBIT              0x80
+#define QM_DQRR_VERB_MASK              0x7f    /* where the verb contains; */
+#define QM_DQRR_VERB_FRAME_DEQUEUE     0x60    /* "this format" */
+#define QM_DQRR_STAT_FQ_EMPTY          0x80    /* FQ empty */
+#define QM_DQRR_STAT_FQ_HELDACTIVE     0x40    /* FQ held active */
+#define QM_DQRR_STAT_FQ_FORCEELIGIBLE  0x20    /* FQ was force-eligible'd */
+#define QM_DQRR_STAT_FD_VALID          0x10    /* has a non-NULL FD */
+#define QM_DQRR_STAT_UNSCHEDULED       0x02    /* Unscheduled dequeue */
+#define QM_DQRR_STAT_DQCR_EXPIRED      0x01    /* VDQCR or PDQCR expired*/
+
+/* "ERN Message Response" */
+/* "FQ State Change Notification" */
+union qm_mr_entry {
+       struct {
+               u8 verb;
+               u8 __reserved[63];
+       };
+       struct {
+               u8 verb;
+               u8 dca;
+               u16 seqnum;
+               u8 rc;          /* Rej Code: 8-bit */
+               u8 orp_hi;      /* ORP: 24-bit */
+               u16 orp_lo;
+               u32 fqid;       /* 24-bit */
+               u32 tag;
+               struct qm_fd fd;
+               u8 __reserved1[32];
+       } __packed ern;
+       struct {
+               u8 verb;
+               u8 fqs;         /* Frame Queue Status */
+               u8 __reserved1[6];
+               u32 fqid;       /* 24-bit */
+               u32 contextB;
+               u8 __reserved2[48];
+       } __packed fq;          /* FQRN/FQRNI/FQRL/FQPN */
+};
+#define QM_MR_VERB_VBIT                        0x80
+/*
+ * ERNs originating from direct-connect portals ("dcern") use 0x20 as a verb
+ * which would be invalid as a s/w enqueue verb. A s/w ERN can be distinguished
+ * from the other MR types by noting if the 0x20 bit is unset.
+ */
+#define QM_MR_VERB_TYPE_MASK           0x27
+#define QM_MR_VERB_DC_ERN              0x20
+#define QM_MR_VERB_FQRN                        0x21
+#define QM_MR_VERB_FQRNI               0x22
+#define QM_MR_VERB_FQRL                        0x23
+#define QM_MR_VERB_FQPN                        0x24
+#define QM_MR_RC_MASK                  0xf0    /* contains one of; */
+#define QM_MR_RC_CGR_TAILDROP          0x00
+#define QM_MR_RC_WRED                  0x10
+#define QM_MR_RC_ERROR                 0x20
+#define QM_MR_RC_ORPWINDOW_EARLY       0x30
+#define QM_MR_RC_ORPWINDOW_LATE                0x40
+#define QM_MR_RC_FQ_TAILDROP           0x50
+#define QM_MR_RC_ORPWINDOW_RETIRED     0x60
+#define QM_MR_RC_ORP_ZERO              0x70
+#define QM_MR_FQS_ORLPRESENT           0x02    /* ORL fragments to come */
+#define QM_MR_FQS_NOTEMPTY             0x01    /* FQ has enqueued frames */
+
+/*
+ * An identical structure of FQD fields is present in the "Init FQ" command and
+ * the "Query FQ" result, it's suctioned out into the "struct qm_fqd" type.
+ * Within that, the 'stashing' and 'taildrop' pieces are also factored out, the
+ * latter has two inlines to assist with converting to/from the mant+exp
+ * representation.
+ */
+struct qm_fqd_stashing {
+       /* See QM_STASHING_EXCL_<...> */
+       u8 exclusive;
+       /* Numbers of cachelines */
+       u8 cl; /* _res[6-7], as[4-5], ds[2-3], cs[0-1] */
+};
+
+struct qm_fqd_oac {
+       /* "Overhead Accounting Control", see QM_OAC_<...> */
+       u8 oac; /* oac[6-7], _res[0-5] */
+       /* Two's-complement value (-128 to +127) */
+       s8 oal; /* "Overhead Accounting Length" */
+};
+
+struct qm_fqd {
+       /* _res[6-7], orprws[3-5], oa[2], olws[0-1] */
+       u8 orpc;
+       u8 cgid;
+       __be16 fq_ctrl; /* See QM_FQCTRL_<...> */
+       __be16 dest_wq; /* channel[3-15], wq[0-2] */
+       __be16 ics_cred; /* 15-bit */
+       /*
+        * For "Initialize Frame Queue" commands, the write-enable mask
+        * determines whether 'td' or 'oac_init' is observed. For query
+        * commands, this field is always 'td', and 'oac_query' (below) reflects
+        * the Overhead ACcounting values.
+        */
+       union {
+               __be16 td; /* "Taildrop": _res[13-15], mant[5-12], exp[0-4] */
+               struct qm_fqd_oac oac_init;
+       };
+       __be32 context_b;
+       union {
+               /* Treat it as 64-bit opaque */
+               __be64 opaque;
+               struct {
+                       __be32 hi;
+                       __be32 lo;
+               };
+               /* Treat it as s/w portal stashing config */
+               /* see "FQD Context_A field used for [...]" */
+               struct {
+                       struct qm_fqd_stashing stashing;
+                       /*
+                        * 48-bit address of FQ context to
+                        * stash, must be cacheline-aligned
+                        */
+                       __be16 context_hi;
+                       __be32 context_lo;
+               } __packed;
+       } context_a;
+       struct qm_fqd_oac oac_query;
+} __packed;
+
+#define QM_FQD_CHAN_OFF                3
+#define QM_FQD_WQ_MASK         GENMASK(2, 0)
+#define QM_FQD_TD_EXP_MASK     GENMASK(4, 0)
+#define QM_FQD_TD_MANT_OFF     5
+#define QM_FQD_TD_MANT_MASK    GENMASK(12, 5)
+#define QM_FQD_TD_MAX          0xe0000000
+#define QM_FQD_TD_MANT_MAX     0xff
+#define QM_FQD_OAC_OFF         6
+#define QM_FQD_AS_OFF          4
+#define QM_FQD_DS_OFF          2
+#define QM_FQD_XS_MASK         0x3
+
+/* 64-bit converters for context_hi/lo */
+static inline u64 qm_fqd_stashing_get64(const struct qm_fqd *fqd)
+{
+       return be64_to_cpu(fqd->context_a.opaque) & 0xffffffffffffULL;
+}
+
+static inline dma_addr_t qm_fqd_stashing_addr(const struct qm_fqd *fqd)
+{
+       return be64_to_cpu(fqd->context_a.opaque) & 0xffffffffffffULL;
+}
+
+static inline u64 qm_fqd_context_a_get64(const struct qm_fqd *fqd)
+{
+       return qm_fqd_stashing_get64(fqd);
+}
+
+static inline void qm_fqd_stashing_set64(struct qm_fqd *fqd, u64 addr)
+{
+       fqd->context_a.context_hi = upper_32_bits(addr);
+       fqd->context_a.context_lo = lower_32_bits(addr);
+}
+
+static inline void qm_fqd_context_a_set64(struct qm_fqd *fqd, u64 addr)
+{
+       fqd->context_a.hi = cpu_to_be16(upper_32_bits(addr));
+       fqd->context_a.lo = cpu_to_be32(lower_32_bits(addr));
+}
+
+/* convert a threshold value into mant+exp representation */
+static inline int qm_fqd_set_taildrop(struct qm_fqd *fqd, u32 val,
+                                     int roundup)
+{
+       u32 e = 0;
+       int td, oddbit = 0;
+
+       if (val > QM_FQD_TD_MAX)
+               return -ERANGE;
+
+       while (val > QM_FQD_TD_MANT_MAX) {
+               oddbit = val & 1;
+               val >>= 1;
+               e++;
+               if (roundup && oddbit)
+                       val++;
+       }
+
+       td = (val << QM_FQD_TD_MANT_OFF) & QM_FQD_TD_MANT_MASK;
+       td |= (e & QM_FQD_TD_EXP_MASK);
+       fqd->td = cpu_to_be16(td);
+       return 0;
+}
+/* and the other direction */
+static inline int qm_fqd_get_taildrop(const struct qm_fqd *fqd)
+{
+       int td = be16_to_cpu(fqd->td);
+
+       return ((td & QM_FQD_TD_MANT_MASK) >> QM_FQD_TD_MANT_OFF)
+               << (td & QM_FQD_TD_EXP_MASK);
+}
+
+static inline void qm_fqd_set_stashing(struct qm_fqd *fqd, u8 as, u8 ds, u8 cs)
+{
+       struct qm_fqd_stashing *st = &fqd->context_a.stashing;
+
+       st->cl = ((as & QM_FQD_XS_MASK) << QM_FQD_AS_OFF) |
+                ((ds & QM_FQD_XS_MASK) << QM_FQD_DS_OFF) |
+                (cs & QM_FQD_XS_MASK);
+}
+
+static inline u8 qm_fqd_get_stashing(const struct qm_fqd *fqd)
+{
+       return fqd->context_a.stashing.cl;
+}
+
+static inline void qm_fqd_set_oac(struct qm_fqd *fqd, u8 val)
+{
+       fqd->oac_init.oac = val << QM_FQD_OAC_OFF;
+}
+
+static inline void qm_fqd_set_oal(struct qm_fqd *fqd, s8 val)
+{
+       fqd->oac_init.oal = val;
+}
+
+static inline void qm_fqd_set_destwq(struct qm_fqd *fqd, int ch, int wq)
+{
+       fqd->dest_wq = cpu_to_be16((ch << QM_FQD_CHAN_OFF) |
+                                  (wq & QM_FQD_WQ_MASK));
+}
+
+static inline int qm_fqd_get_chan(const struct qm_fqd *fqd)
+{
+       return be16_to_cpu(fqd->dest_wq) >> QM_FQD_CHAN_OFF;
+}
+
+static inline int qm_fqd_get_wq(const struct qm_fqd *fqd)
+{
+       return be16_to_cpu(fqd->dest_wq) & QM_FQD_WQ_MASK;
+}
+
+/* See "Frame Queue Descriptor (FQD)" */
+/* Frame Queue Descriptor (FQD) field 'fq_ctrl' uses these constants */
+#define QM_FQCTRL_MASK         0x07ff  /* 'fq_ctrl' flags; */
+#define QM_FQCTRL_CGE          0x0400  /* Congestion Group Enable */
+#define QM_FQCTRL_TDE          0x0200  /* Tail-Drop Enable */
+#define QM_FQCTRL_CTXASTASHING 0x0080  /* Context-A stashing */
+#define QM_FQCTRL_CPCSTASH     0x0040  /* CPC Stash Enable */
+#define QM_FQCTRL_FORCESFDR    0x0008  /* High-priority SFDRs */
+#define QM_FQCTRL_AVOIDBLOCK   0x0004  /* Don't block active */
+#define QM_FQCTRL_HOLDACTIVE   0x0002  /* Hold active in portal */
+#define QM_FQCTRL_PREFERINCACHE        0x0001  /* Aggressively cache FQD */
+#define QM_FQCTRL_LOCKINCACHE  QM_FQCTRL_PREFERINCACHE /* older naming */
+
+/* See "FQD Context_A field used for [...] */
+/* Frame Queue Descriptor (FQD) field 'CONTEXT_A' uses these constants */
+#define QM_STASHING_EXCL_ANNOTATION    0x04
+#define QM_STASHING_EXCL_DATA          0x02
+#define QM_STASHING_EXCL_CTX           0x01
+
+/* See "Intra Class Scheduling" */
+/* FQD field 'OAC' (Overhead ACcounting) uses these constants */
+#define QM_OAC_ICS             0x2 /* Accounting for Intra-Class Scheduling */
+#define QM_OAC_CG              0x1 /* Accounting for Congestion Groups */
+
+/*
+ * This struct represents the 32-bit "WR_PARM_[GYR]" parameters in CGR fields
+ * and associated commands/responses. The WRED parameters are calculated from
+ * these fields as follows;
+ *   MaxTH = MA * (2 ^ Mn)
+ *   Slope = SA / (2 ^ Sn)
+ *    MaxP = 4 * (Pn + 1)
+ */
+struct qm_cgr_wr_parm {
+       /* MA[24-31], Mn[19-23], SA[12-18], Sn[6-11], Pn[0-5] */
+       u32 word;
+};
+/*
+ * This struct represents the 13-bit "CS_THRES" CGR field. In the corresponding
+ * management commands, this is padded to a 16-bit structure field, so that's
+ * how we represent it here. The congestion state threshold is calculated from
+ * these fields as follows;
+ *   CS threshold = TA * (2 ^ Tn)
+ */
+struct qm_cgr_cs_thres {
+       /* _res[13-15], TA[5-12], Tn[0-4] */
+       u16 word;
+};
+/*
+ * This identical structure of CGR fields is present in the "Init/Modify CGR"
+ * commands and the "Query CGR" result. It's suctioned out here into its own
+ * struct.
+ */
+struct __qm_mc_cgr {
+       struct qm_cgr_wr_parm wr_parm_g;
+       struct qm_cgr_wr_parm wr_parm_y;
+       struct qm_cgr_wr_parm wr_parm_r;
+       u8 wr_en_g;     /* boolean, use QM_CGR_EN */
+       u8 wr_en_y;     /* boolean, use QM_CGR_EN */
+       u8 wr_en_r;     /* boolean, use QM_CGR_EN */
+       u8 cscn_en;     /* boolean, use QM_CGR_EN */
+       union {
+               struct {
+                       u16 cscn_targ_upd_ctrl; /* use QM_CSCN_TARG_UDP_ */
+                       u16 cscn_targ_dcp_low;  /* CSCN_TARG_DCP low-16bits */
+               };
+               u32 cscn_targ;  /* use QM_CGR_TARG_* */
+       };
+       u8 cstd_en;     /* boolean, use QM_CGR_EN */
+       u8 cs;          /* boolean, only used in query response */
+       struct qm_cgr_cs_thres cs_thres; /* use qm_cgr_cs_thres_set64() */
+       u8 mode;        /* QMAN_CGR_MODE_FRAME not supported in rev1.0 */
+} __packed;
+#define QM_CGR_EN              0x01 /* For wr_en_*, cscn_en, cstd_en */
+#define QM_CGR_TARG_UDP_CTRL_WRITE_BIT 0x8000 /* value written to portal bit*/
+#define QM_CGR_TARG_UDP_CTRL_DCP       0x4000 /* 0: SWP, 1: DCP */
+#define QM_CGR_TARG_PORTAL(n)  (0x80000000 >> (n)) /* s/w portal, 0-9 */
+#define QM_CGR_TARG_FMAN0      0x00200000 /* direct-connect portal: fman0 */
+#define QM_CGR_TARG_FMAN1      0x00100000 /*                      : fman1 */
+/* Convert CGR thresholds to/from "cs_thres" format */
+static inline u64 qm_cgr_cs_thres_get64(const struct qm_cgr_cs_thres *th)
+{
+       return ((th->word >> 5) & 0xff) << (th->word & 0x1f);
+}
+
+static inline int qm_cgr_cs_thres_set64(struct qm_cgr_cs_thres *th, u64 val,
+                                       int roundup)
+{
+       u32 e = 0;
+       int oddbit = 0;
+
+       while (val > 0xff) {
+               oddbit = val & 1;
+               val >>= 1;
+               e++;
+               if (roundup && oddbit)
+                       val++;
+       }
+       th->word = ((val & 0xff) << 5) | (e & 0x1f);
+       return 0;
+}
+
+/* "Initialize FQ" */
+struct qm_mcc_initfq {
+       u8 __reserved1[2];
+       u16 we_mask;    /* Write Enable Mask */
+       u32 fqid;       /* 24-bit */
+       u16 count;      /* Initialises 'count+1' FQDs */
+       struct qm_fqd fqd; /* the FQD fields go here */
+       u8 __reserved2[30];
+} __packed;
+/* "Initialize/Modify CGR" */
+struct qm_mcc_initcgr {
+       u8 __reserve1[2];
+       u16 we_mask;    /* Write Enable Mask */
+       struct __qm_mc_cgr cgr; /* CGR fields */
+       u8 __reserved2[2];
+       u8 cgid;
+       u8 __reserved3[32];
+} __packed;
+
+/* INITFQ-specific flags */
+#define QM_INITFQ_WE_MASK              0x01ff  /* 'Write Enable' flags; */
+#define QM_INITFQ_WE_OAC               0x0100
+#define QM_INITFQ_WE_ORPC              0x0080
+#define QM_INITFQ_WE_CGID              0x0040
+#define QM_INITFQ_WE_FQCTRL            0x0020
+#define QM_INITFQ_WE_DESTWQ            0x0010
+#define QM_INITFQ_WE_ICSCRED           0x0008
+#define QM_INITFQ_WE_TDTHRESH          0x0004
+#define QM_INITFQ_WE_CONTEXTB          0x0002
+#define QM_INITFQ_WE_CONTEXTA          0x0001
+/* INITCGR/MODIFYCGR-specific flags */
+#define QM_CGR_WE_MASK                 0x07ff  /* 'Write Enable Mask'; */
+#define QM_CGR_WE_WR_PARM_G            0x0400
+#define QM_CGR_WE_WR_PARM_Y            0x0200
+#define QM_CGR_WE_WR_PARM_R            0x0100
+#define QM_CGR_WE_WR_EN_G              0x0080
+#define QM_CGR_WE_WR_EN_Y              0x0040
+#define QM_CGR_WE_WR_EN_R              0x0020
+#define QM_CGR_WE_CSCN_EN              0x0010
+#define QM_CGR_WE_CSCN_TARG            0x0008
+#define QM_CGR_WE_CSTD_EN              0x0004
+#define QM_CGR_WE_CS_THRES             0x0002
+#define QM_CGR_WE_MODE                 0x0001
+
+#define QMAN_CGR_FLAG_USE_INIT      0x00000001
+
+       /* Portal and Frame Queues */
+/* Represents a managed portal */
+struct qman_portal;
+
+/*
+ * This object type represents QMan frame queue descriptors (FQD), it is
+ * cacheline-aligned, and initialised by qman_create_fq(). The structure is
+ * defined further down.
+ */
+struct qman_fq;
+
+/*
+ * This object type represents a QMan congestion group, it is defined further
+ * down.
+ */
+struct qman_cgr;
+
+/*
+ * This enum, and the callback type that returns it, are used when handling
+ * dequeued frames via DQRR. Note that for "null" callbacks registered with the
+ * portal object (for handling dequeues that do not demux because contextB is
+ * NULL), the return value *MUST* be qman_cb_dqrr_consume.
+ */
+enum qman_cb_dqrr_result {
+       /* DQRR entry can be consumed */
+       qman_cb_dqrr_consume,
+       /* Like _consume, but requests parking - FQ must be held-active */
+       qman_cb_dqrr_park,
+       /* Does not consume, for DCA mode only. */
+       qman_cb_dqrr_defer,
+       /*
+        * Stop processing without consuming this ring entry. Exits the current
+        * qman_p_poll_dqrr() or interrupt-handling, as appropriate. If within
+        * an interrupt handler, the callback would typically call
+        * qman_irqsource_remove(QM_PIRQ_DQRI) before returning this value,
+        * otherwise the interrupt will reassert immediately.
+        */
+       qman_cb_dqrr_stop,
+       /* Like qman_cb_dqrr_stop, but consumes the current entry. */
+       qman_cb_dqrr_consume_stop
+};
+typedef enum qman_cb_dqrr_result (*qman_cb_dqrr)(struct qman_portal *qm,
+                                       struct qman_fq *fq,
+                                       const struct qm_dqrr_entry *dqrr);
+
+/*
+ * This callback type is used when handling ERNs, FQRNs and FQRLs via MR. They
+ * are always consumed after the callback returns.
+ */
+typedef void (*qman_cb_mr)(struct qman_portal *qm, struct qman_fq *fq,
+                          const union qm_mr_entry *msg);
+
+/*
+ * s/w-visible states. Ie. tentatively scheduled + truly scheduled + active +
+ * held-active + held-suspended are just "sched". Things like "retired" will not
+ * be assumed until it is complete (ie. QMAN_FQ_STATE_CHANGING is set until
+ * then, to indicate it's completing and to gate attempts to retry the retire
+ * command). Note, park commands do not set QMAN_FQ_STATE_CHANGING because it's
+ * technically impossible in the case of enqueue DCAs (which refer to DQRR ring
+ * index rather than the FQ that ring entry corresponds to), so repeated park
+ * commands are allowed (if you're silly enough to try) but won't change FQ
+ * state, and the resulting park notifications move FQs from "sched" to
+ * "parked".
+ */
+enum qman_fq_state {
+       qman_fq_state_oos,
+       qman_fq_state_parked,
+       qman_fq_state_sched,
+       qman_fq_state_retired
+};
+
+#define QMAN_FQ_STATE_CHANGING      0x80000000 /* 'state' is changing */
+#define QMAN_FQ_STATE_NE            0x40000000 /* retired FQ isn't empty */
+#define QMAN_FQ_STATE_ORL           0x20000000 /* retired FQ has ORL */
+#define QMAN_FQ_STATE_BLOCKOOS      0xe0000000 /* if any are set, no OOS */
+#define QMAN_FQ_STATE_CGR_EN        0x10000000 /* CGR enabled */
+#define QMAN_FQ_STATE_VDQCR         0x08000000 /* being volatile dequeued */
+
+/*
+ * Frame queue objects (struct qman_fq) are stored within memory passed to
+ * qman_create_fq(), as this allows stashing of caller-provided demux callback
+ * pointers at no extra cost to stashing of (driver-internal) FQ state. If the
+ * caller wishes to add per-FQ state and have it benefit from dequeue-stashing,
+ * they should;
+ *
+ * (a) extend the qman_fq structure with their state; eg.
+ *
+ *     // myfq is allocated and driver_fq callbacks filled in;
+ *     struct my_fq {
+ *        struct qman_fq base;
+ *        int an_extra_field;
+ *        [ ... add other fields to be associated with each FQ ...]
+ *     } *myfq = some_my_fq_allocator();
+ *     struct qman_fq *fq = qman_create_fq(fqid, flags, &myfq->base);
+ *
+ *     // in a dequeue callback, access extra fields from 'fq' via a cast;
+ *     struct my_fq *myfq = (struct my_fq *)fq;
+ *     do_something_with(myfq->an_extra_field);
+ *     [...]
+ *
+ * (b) when and if configuring the FQ for context stashing, specify how ever
+ *     many cachelines are required to stash 'struct my_fq', to accelerate not
+ *     only the QMan driver but the callback as well.
+ */
+
+struct qman_fq_cb {
+       qman_cb_dqrr dqrr;      /* for dequeued frames */
+       qman_cb_mr ern;         /* for s/w ERNs */
+       qman_cb_mr fqs;         /* frame-queue state changes*/
+};
+
+struct qman_fq {
+       /* Caller of qman_create_fq() provides these demux callbacks */
+       struct qman_fq_cb cb;
+       /*
+        * These are internal to the driver, don't touch. In particular, they
+        * may change, be removed, or extended (so you shouldn't rely on
+        * sizeof(qman_fq) being a constant).
+        */
+       u32 fqid, idx;
+       unsigned long flags;
+       enum qman_fq_state state;
+       int cgr_groupid;
+};
+
+/*
+ * This callback type is used when handling congestion group entry/exit.
+ * 'congested' is non-zero on congestion-entry, and zero on congestion-exit.
+ */
+typedef void (*qman_cb_cgr)(struct qman_portal *qm,
+                           struct qman_cgr *cgr, int congested);
+
+struct qman_cgr {
+       /* Set these prior to qman_create_cgr() */
+       u32 cgrid; /* 0..255, but u32 to allow specials like -1, 256, etc.*/
+       qman_cb_cgr cb;
+       /* These are private to the driver */
+       u16 chan; /* portal channel this object is created on */
+       struct list_head node;
+};
+
+/* Flags to qman_create_fq() */
+#define QMAN_FQ_FLAG_NO_ENQUEUE             0x00000001 /* can't enqueue */
+#define QMAN_FQ_FLAG_NO_MODIFY      0x00000002 /* can only enqueue */
+#define QMAN_FQ_FLAG_TO_DCPORTAL     0x00000004 /* consumed by CAAM/PME/Fman */
+#define QMAN_FQ_FLAG_DYNAMIC_FQID    0x00000020 /* (de)allocate fqid */
+
+/* Flags to qman_init_fq() */
+#define QMAN_INITFQ_FLAG_SCHED      0x00000001 /* schedule rather than park */
+#define QMAN_INITFQ_FLAG_LOCAL      0x00000004 /* set dest portal */
+
+       /* Portal Management */
+/**
+ * qman_p_irqsource_add - add processing sources to be interrupt-driven
+ * @bits: bitmask of QM_PIRQ_**I processing sources
+ *
+ * Adds processing sources that should be interrupt-driven (rather than
+ * processed via qman_poll_***() functions).
+ */
+void qman_p_irqsource_add(struct qman_portal *p, u32 bits);
+
+/**
+ * qman_p_irqsource_remove - remove processing sources from being int-driven
+ * @bits: bitmask of QM_PIRQ_**I processing sources
+ *
+ * Removes processing sources from being interrupt-driven, so that they will
+ * instead be processed via qman_poll_***() functions.
+ */
+void qman_p_irqsource_remove(struct qman_portal *p, u32 bits);
+
+/**
+ * qman_affine_cpus - return a mask of cpus that have affine portals
+ */
+const cpumask_t *qman_affine_cpus(void);
+
+/**
+ * qman_affine_channel - return the channel ID of an portal
+ * @cpu: the cpu whose affine portal is the subject of the query
+ *
+ * If @cpu is -1, the affine portal for the current CPU will be used. It is a
+ * bug to call this function for any value of @cpu (other than -1) that is not a
+ * member of the mask returned from qman_affine_cpus().
+ */
+u16 qman_affine_channel(int cpu);
+
+/**
+ * qman_get_affine_portal - return the portal pointer affine to cpu
+ * @cpu: the cpu whose affine portal is the subject of the query
+ */
+struct qman_portal *qman_get_affine_portal(int cpu);
+
+/**
+ * qman_p_poll_dqrr - process DQRR (fast-path) entries
+ * @limit: the maximum number of DQRR entries to process
+ *
+ * Use of this function requires that DQRR processing not be interrupt-driven.
+ * The return value represents the number of DQRR entries processed.
+ */
+int qman_p_poll_dqrr(struct qman_portal *p, unsigned int limit);
+
+/**
+ * qman_p_static_dequeue_add - Add pool channels to the portal SDQCR
+ * @pools: bit-mask of pool channels, using QM_SDQCR_CHANNELS_POOL(n)
+ *
+ * Adds a set of pool channels to the portal's static dequeue command register
+ * (SDQCR). The requested pools are limited to those the portal has dequeue
+ * access to.
+ */
+void qman_p_static_dequeue_add(struct qman_portal *p, u32 pools);
+
+       /* FQ management */
+/**
+ * qman_create_fq - Allocates a FQ
+ * @fqid: the index of the FQD to encapsulate, must be "Out of Service"
+ * @flags: bit-mask of QMAN_FQ_FLAG_*** options
+ * @fq: memory for storing the 'fq', with callbacks filled in
+ *
+ * Creates a frame queue object for the given @fqid, unless the
+ * QMAN_FQ_FLAG_DYNAMIC_FQID flag is set in @flags, in which case a FQID is
+ * dynamically allocated (or the function fails if none are available). Once
+ * created, the caller should not touch the memory at 'fq' except as extended to
+ * adjacent memory for user-defined fields (see the definition of "struct
+ * qman_fq" for more info). NO_MODIFY is only intended for enqueuing to
+ * pre-existing frame-queues that aren't to be otherwise interfered with, it
+ * prevents all other modifications to the frame queue. The TO_DCPORTAL flag
+ * causes the driver to honour any contextB modifications requested in the
+ * qm_init_fq() API, as this indicates the frame queue will be consumed by a
+ * direct-connect portal (PME, CAAM, or Fman). When frame queues are consumed by
+ * software portals, the contextB field is controlled by the driver and can't be
+ * modified by the caller.
+ */
+int qman_create_fq(u32 fqid, u32 flags, struct qman_fq *fq);
+
+/**
+ * qman_destroy_fq - Deallocates a FQ
+ * @fq: the frame queue object to release
+ *
+ * The memory for this frame queue object ('fq' provided in qman_create_fq()) is
+ * not deallocated but the caller regains ownership, to do with as desired. The
+ * FQ must be in the 'out-of-service' or in the 'parked' state.
+ */
+void qman_destroy_fq(struct qman_fq *fq);
+
+/**
+ * qman_fq_fqid - Queries the frame queue ID of a FQ object
+ * @fq: the frame queue object to query
+ */
+u32 qman_fq_fqid(struct qman_fq *fq);
+
+/**
+ * qman_init_fq - Initialises FQ fields, leaves the FQ "parked" or "scheduled"
+ * @fq: the frame queue object to modify, must be 'parked' or new.
+ * @flags: bit-mask of QMAN_INITFQ_FLAG_*** options
+ * @opts: the FQ-modification settings, as defined in the low-level API
+ *
+ * The @opts parameter comes from the low-level portal API. Select
+ * QMAN_INITFQ_FLAG_SCHED in @flags to cause the frame queue to be scheduled
+ * rather than parked. NB, @opts can be NULL.
+ *
+ * Note that some fields and options within @opts may be ignored or overwritten
+ * by the driver;
+ * 1. the 'count' and 'fqid' fields are always ignored (this operation only
+ * affects one frame queue: @fq).
+ * 2. the QM_INITFQ_WE_CONTEXTB option of the 'we_mask' field and the associated
+ * 'fqd' structure's 'context_b' field are sometimes overwritten;
+ *   - if @fq was not created with QMAN_FQ_FLAG_TO_DCPORTAL, then context_b is
+ *     initialised to a value used by the driver for demux.
+ *   - if context_b is initialised for demux, so is context_a in case stashing
+ *     is requested (see item 4).
+ * (So caller control of context_b is only possible for TO_DCPORTAL frame queue
+ * objects.)
+ * 3. if @flags contains QMAN_INITFQ_FLAG_LOCAL, the 'fqd' structure's
+ * 'dest::channel' field will be overwritten to match the portal used to issue
+ * the command. If the WE_DESTWQ write-enable bit had already been set by the
+ * caller, the channel workqueue will be left as-is, otherwise the write-enable
+ * bit is set and the workqueue is set to a default of 4. If the "LOCAL" flag
+ * isn't set, the destination channel/workqueue fields and the write-enable bit
+ * are left as-is.
+ * 4. if the driver overwrites context_a/b for demux, then if
+ * QM_INITFQ_WE_CONTEXTA is set, the driver will only overwrite
+ * context_a.address fields and will leave the stashing fields provided by the
+ * user alone, otherwise it will zero out the context_a.stashing fields.
+ */
+int qman_init_fq(struct qman_fq *fq, u32 flags, struct qm_mcc_initfq *opts);
+
+/**
+ * qman_schedule_fq - Schedules a FQ
+ * @fq: the frame queue object to schedule, must be 'parked'
+ *
+ * Schedules the frame queue, which must be Parked, which takes it to
+ * Tentatively-Scheduled or Truly-Scheduled depending on its fill-level.
+ */
+int qman_schedule_fq(struct qman_fq *fq);
+
+/**
+ * qman_retire_fq - Retires a FQ
+ * @fq: the frame queue object to retire
+ * @flags: FQ flags (QMAN_FQ_STATE*) if retirement completes immediately
+ *
+ * Retires the frame queue. This returns zero if it succeeds immediately, +1 if
+ * the retirement was started asynchronously, otherwise it returns negative for
+ * failure. When this function returns zero, @flags is set to indicate whether
+ * the retired FQ is empty and/or whether it has any ORL fragments (to show up
+ * as ERNs). Otherwise the corresponding flags will be known when a subsequent
+ * FQRN message shows up on the portal's message ring.
+ *
+ * NB, if the retirement is asynchronous (the FQ was in the Truly Scheduled or
+ * Active state), the completion will be via the message ring as a FQRN - but
+ * the corresponding callback may occur before this function returns!! Ie. the
+ * caller should be prepared to accept the callback as the function is called,
+ * not only once it has returned.
+ */
+int qman_retire_fq(struct qman_fq *fq, u32 *flags);
+
+/**
+ * qman_oos_fq - Puts a FQ "out of service"
+ * @fq: the frame queue object to be put out-of-service, must be 'retired'
+ *
+ * The frame queue must be retired and empty, and if any order restoration list
+ * was released as ERNs at the time of retirement, they must all be consumed.
+ */
+int qman_oos_fq(struct qman_fq *fq);
+
+/**
+ * qman_enqueue - Enqueue a frame to a frame queue
+ * @fq: the frame queue object to enqueue to
+ * @fd: a descriptor of the frame to be enqueued
+ *
+ * Fills an entry in the EQCR of portal @qm to enqueue the frame described by
+ * @fd. The descriptor details are copied from @fd to the EQCR entry, the 'pid'
+ * field is ignored. The return value is non-zero on error, such as ring full.
+ */
+int qman_enqueue(struct qman_fq *fq, const struct qm_fd *fd);
+
+/**
+ * qman_alloc_fqid_range - Allocate a contiguous range of FQIDs
+ * @result: is set by the API to the base FQID of the allocated range
+ * @count: the number of FQIDs required
+ *
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_alloc_fqid_range(u32 *result, u32 count);
+#define qman_alloc_fqid(result) qman_alloc_fqid_range(result, 1)
+
+/**
+ * qman_release_fqid - Release the specified frame queue ID
+ * @fqid: the FQID to be released back to the resource pool
+ *
+ * This function can also be used to seed the allocator with
+ * FQID ranges that it can subsequently allocate from.
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_release_fqid(u32 fqid);
+
+       /* Pool-channel management */
+/**
+ * qman_alloc_pool_range - Allocate a contiguous range of pool-channel IDs
+ * @result: is set by the API to the base pool-channel ID of the allocated range
+ * @count: the number of pool-channel IDs required
+ *
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_alloc_pool_range(u32 *result, u32 count);
+#define qman_alloc_pool(result) qman_alloc_pool_range(result, 1)
+
+/**
+ * qman_release_pool - Release the specified pool-channel ID
+ * @id: the pool-chan ID to be released back to the resource pool
+ *
+ * This function can also be used to seed the allocator with
+ * pool-channel ID ranges that it can subsequently allocate from.
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_release_pool(u32 id);
+
+       /* CGR management */
+/**
+ * qman_create_cgr - Register a congestion group object
+ * @cgr: the 'cgr' object, with fields filled in
+ * @flags: QMAN_CGR_FLAG_* values
+ * @opts: optional state of CGR settings
+ *
+ * Registers this object to receiving congestion entry/exit callbacks on the
+ * portal affine to the cpu portal on which this API is executed. If opts is
+ * NULL then only the callback (cgr->cb) function is registered. If @flags
+ * contains QMAN_CGR_FLAG_USE_INIT, then an init hw command (which will reset
+ * any unspecified parameters) will be used rather than a modify hw hardware
+ * (which only modifies the specified parameters).
+ */
+int qman_create_cgr(struct qman_cgr *cgr, u32 flags,
+                   struct qm_mcc_initcgr *opts);
+
+/**
+ * qman_delete_cgr - Deregisters a congestion group object
+ * @cgr: the 'cgr' object to deregister
+ *
+ * "Unplugs" this CGR object from the portal affine to the cpu on which this API
+ * is executed. This must be excuted on the same affine portal on which it was
+ * created.
+ */
+int qman_delete_cgr(struct qman_cgr *cgr);
+
+/**
+ * qman_delete_cgr_safe - Deregisters a congestion group object from any CPU
+ * @cgr: the 'cgr' object to deregister
+ *
+ * This will select the proper CPU and run there qman_delete_cgr().
+ */
+void qman_delete_cgr_safe(struct qman_cgr *cgr);
+
+/**
+ * qman_query_cgr_congested - Queries CGR's congestion status
+ * @cgr: the 'cgr' object to query
+ * @result: returns 'cgr's congestion status, 1 (true) if congested
+ */
+int qman_query_cgr_congested(struct qman_cgr *cgr, bool *result);
+
+/**
+ * qman_alloc_cgrid_range - Allocate a contiguous range of CGR IDs
+ * @result: is set by the API to the base CGR ID of the allocated range
+ * @count: the number of CGR IDs required
+ *
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_alloc_cgrid_range(u32 *result, u32 count);
+#define qman_alloc_cgrid(result) qman_alloc_cgrid_range(result, 1)
+
+/**
+ * qman_release_cgrid - Release the specified CGR ID
+ * @id: the CGR ID to be released back to the resource pool
+ *
+ * This function can also be used to seed the allocator with
+ * CGR ID ranges that it can subsequently allocate from.
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_release_cgrid(u32 id);
+
+#endif /* __FSL_QMAN_H */
diff --git a/include/trace/events/cgroup.h b/include/trace/events/cgroup.h
new file mode 100644 (file)
index 0000000..ab68640
--- /dev/null
@@ -0,0 +1,163 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cgroup
+
+#if !defined(_TRACE_CGROUP_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_CGROUP_H
+
+#include <linux/cgroup.h>
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(cgroup_root,
+
+       TP_PROTO(struct cgroup_root *root),
+
+       TP_ARGS(root),
+
+       TP_STRUCT__entry(
+               __field(        int,            root                    )
+               __field(        u16,            ss_mask                 )
+               __string(       name,           root->name              )
+       ),
+
+       TP_fast_assign(
+               __entry->root = root->hierarchy_id;
+               __entry->ss_mask = root->subsys_mask;
+               __assign_str(name, root->name);
+       ),
+
+       TP_printk("root=%d ss_mask=%#x name=%s",
+                 __entry->root, __entry->ss_mask, __get_str(name))
+);
+
+DEFINE_EVENT(cgroup_root, cgroup_setup_root,
+
+       TP_PROTO(struct cgroup_root *root),
+
+       TP_ARGS(root)
+);
+
+DEFINE_EVENT(cgroup_root, cgroup_destroy_root,
+
+       TP_PROTO(struct cgroup_root *root),
+
+       TP_ARGS(root)
+);
+
+DEFINE_EVENT(cgroup_root, cgroup_remount,
+
+       TP_PROTO(struct cgroup_root *root),
+
+       TP_ARGS(root)
+);
+
+DECLARE_EVENT_CLASS(cgroup,
+
+       TP_PROTO(struct cgroup *cgrp),
+
+       TP_ARGS(cgrp),
+
+       TP_STRUCT__entry(
+               __field(        int,            root                    )
+               __field(        int,            id                      )
+               __field(        int,            level                   )
+               __dynamic_array(char,           path,
+                               cgrp->kn ? cgroup_path(cgrp, NULL, 0) + 1
+                                        : strlen("(null)"))
+       ),
+
+       TP_fast_assign(
+               __entry->root = cgrp->root->hierarchy_id;
+               __entry->id = cgrp->id;
+               __entry->level = cgrp->level;
+               if (cgrp->kn)
+                       cgroup_path(cgrp, __get_dynamic_array(path),
+                                   __get_dynamic_array_len(path));
+               else
+                       __assign_str(path, "(null)");
+       ),
+
+       TP_printk("root=%d id=%d level=%d path=%s",
+                 __entry->root, __entry->id, __entry->level, __get_str(path))
+);
+
+DEFINE_EVENT(cgroup, cgroup_mkdir,
+
+       TP_PROTO(struct cgroup *cgroup),
+
+       TP_ARGS(cgroup)
+);
+
+DEFINE_EVENT(cgroup, cgroup_rmdir,
+
+       TP_PROTO(struct cgroup *cgroup),
+
+       TP_ARGS(cgroup)
+);
+
+DEFINE_EVENT(cgroup, cgroup_release,
+
+       TP_PROTO(struct cgroup *cgroup),
+
+       TP_ARGS(cgroup)
+);
+
+DEFINE_EVENT(cgroup, cgroup_rename,
+
+       TP_PROTO(struct cgroup *cgroup),
+
+       TP_ARGS(cgroup)
+);
+
+DECLARE_EVENT_CLASS(cgroup_migrate,
+
+       TP_PROTO(struct cgroup *dst_cgrp, struct task_struct *task, bool threadgroup),
+
+       TP_ARGS(dst_cgrp, task, threadgroup),
+
+       TP_STRUCT__entry(
+               __field(        int,            dst_root                )
+               __field(        int,            dst_id                  )
+               __field(        int,            dst_level               )
+               __dynamic_array(char,           dst_path,
+                               dst_cgrp->kn ? cgroup_path(dst_cgrp, NULL, 0) + 1
+                                            : strlen("(null)"))
+               __field(        int,            pid                     )
+               __string(       comm,           task->comm              )
+       ),
+
+       TP_fast_assign(
+               __entry->dst_root = dst_cgrp->root->hierarchy_id;
+               __entry->dst_id = dst_cgrp->id;
+               __entry->dst_level = dst_cgrp->level;
+               if (dst_cgrp->kn)
+                       cgroup_path(dst_cgrp, __get_dynamic_array(dst_path),
+                                   __get_dynamic_array_len(dst_path));
+               else
+                       __assign_str(dst_path, "(null)");
+               __entry->pid = task->pid;
+               __assign_str(comm, task->comm);
+       ),
+
+       TP_printk("dst_root=%d dst_id=%d dst_level=%d dst_path=%s pid=%d comm=%s",
+                 __entry->dst_root, __entry->dst_id, __entry->dst_level,
+                 __get_str(dst_path), __entry->pid, __get_str(comm))
+);
+
+DEFINE_EVENT(cgroup_migrate, cgroup_attach_task,
+
+       TP_PROTO(struct cgroup *dst_cgrp, struct task_struct *task, bool threadgroup),
+
+       TP_ARGS(dst_cgrp, task, threadgroup)
+);
+
+DEFINE_EVENT(cgroup_migrate, cgroup_transfer_tasks,
+
+       TP_PROTO(struct cgroup *dst_cgrp, struct task_struct *task, bool threadgroup),
+
+       TP_ARGS(dst_cgrp, task, threadgroup)
+);
+
+#endif /* _TRACE_CGROUP_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index dbfee7e86ba6429319188a03d618881ae34a9ef0..9b1462e38b821a762b284b44a20a96de9f0930d9 100644 (file)
@@ -730,10 +730,6 @@ __SYSCALL(__NR_pkey_mprotect, sys_pkey_mprotect)
 __SYSCALL(__NR_pkey_alloc,    sys_pkey_alloc)
 #define __NR_pkey_free 290
 __SYSCALL(__NR_pkey_free,     sys_pkey_free)
-#define __NR_pkey_get 291
-//__SYSCALL(__NR_pkey_get,      sys_pkey_get)
-#define __NR_pkey_set 292
-//__SYSCALL(__NR_pkey_set,      sys_pkey_set)
 
 #undef __NR_syscalls
 #define __NR_syscalls 291
index ac5eacd3055b503f9b95e330600608bf5a018cec..db4c253f8011b2f483ddd1ffc09f4f04a93fdc0a 100644 (file)
@@ -239,7 +239,17 @@ struct btrfs_ioctl_fs_info_args {
  * Used by:
  * struct btrfs_ioctl_feature_flags
  */
-#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE        (1ULL << 0)
+#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE                (1ULL << 0)
+/*
+ * Older kernels (< 4.9) on big-endian systems produced broken free space tree
+ * bitmaps, and btrfs-progs also used to corrupt the free space tree (versions
+ * < 4.7.3).  If this bit is clear, then the free space tree cannot be trusted.
+ * btrfs-progs can also intentionally clear this bit to ask the kernel to
+ * rebuild the free space tree, however this might not work on older kernels
+ * that do not know about this bit. If not sure, clear the cache manually on
+ * first mount when booting older kernel versions.
+ */
+#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID  (1ULL << 1)
 
 #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF   (1ULL << 0)
 #define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL  (1ULL << 1)
index 3e445a760f14b384bd78636f5f90b2bcb4f723cf..b075f601919be12218726180837bc7b0d2eb4655 100644 (file)
  */
 #define FALLOC_FL_INSERT_RANGE         0x20
 
+/*
+ * FALLOC_FL_UNSHARE_RANGE is used to unshare shared blocks within the
+ * file size without overwriting any existing data. The purpose of this
+ * call is to preemptively reallocate any blocks that are subject to
+ * copy-on-write.
+ *
+ * Different filesystems may implement different limitations on the
+ * granularity of the operation. Most will limit operations to filesystem
+ * block size boundaries, but this boundary may be larger or smaller
+ * depending on the filesystem and/or the configuration of the filesystem
+ * or file.
+ *
+ * This flag can only be used with allocate-mode fallocate, which is
+ * to say that it cannot be used with the punch, zero, collapse, or
+ * insert range modes.
+ */
+#define FALLOC_FL_UNSHARE_RANGE                0x40
+
 #endif /* _UAPI_FALLOC_H_ */
index 2473272169f2a6237e3bac11023244f8cc7a529d..acb2b6152ba01243a363bbb199a525c28d9e1101 100644 (file)
@@ -158,7 +158,8 @@ struct fsxattr {
        __u32           fsx_extsize;    /* extsize field value (get/set)*/
        __u32           fsx_nextents;   /* nextents field value (get)   */
        __u32           fsx_projid;     /* project identifier (get/set) */
-       unsigned char   fsx_pad[12];
+       __u32           fsx_cowextsize; /* CoW extsize field value (get/set)*/
+       unsigned char   fsx_pad[8];
 };
 
 /*
@@ -179,6 +180,7 @@ struct fsxattr {
 #define FS_XFLAG_NODEFRAG      0x00002000      /* do not defragment */
 #define FS_XFLAG_FILESTREAM    0x00004000      /* use filestream allocator */
 #define FS_XFLAG_DAX           0x00008000      /* use DAX for IO */
+#define FS_XFLAG_COWEXTSIZE    0x00010000      /* CoW extent size allocator hint */
 #define FS_XFLAG_HASATTR       0x80000000      /* no DIFLAG for this   */
 
 /* the read-only stuff doesn't really belong here, but any other place is
index 2b871e0858d9fb23364ab908b22a39118bf5d34e..4ae62796bfdeec2570811ff4e8c43a332f6a6848 100644 (file)
@@ -39,8 +39,9 @@
 #define NFS4_FH_VOL_MIGRATION          0x0004
 #define NFS4_FH_VOL_RENAME             0x0008
 
-#define NFS4_OPEN_RESULT_CONFIRM 0x0002
-#define NFS4_OPEN_RESULT_LOCKTYPE_POSIX 0x0004
+#define NFS4_OPEN_RESULT_CONFIRM               0x0002
+#define NFS4_OPEN_RESULT_LOCKTYPE_POSIX                0x0004
+#define NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK       0x0020
 
 #define NFS4_SHARE_ACCESS_MASK 0x000F
 #define NFS4_SHARE_ACCESS_READ 0x0001
index d812172d1d7b45a7e6fa279db719d75cd3efcc9e..e5a2e68b2236e51e5b7bab0a143a02763054dac6 100644 (file)
  */
 #define PCI_EXP_DEVCAP2                36      /* Device Capabilities 2 */
 #define  PCI_EXP_DEVCAP2_ARI           0x00000020 /* Alternative Routing-ID */
+#define  PCI_EXP_DEVCAP2_ATOMIC_ROUTE  0x00000040 /* Atomic Op routing */
+#define PCI_EXP_DEVCAP2_ATOMIC_COMP64  0x00000100 /* Atomic 64-bit compare */
 #define  PCI_EXP_DEVCAP2_LTR           0x00000800 /* Latency tolerance reporting */
 #define  PCI_EXP_DEVCAP2_OBFF_MASK     0x000c0000 /* OBFF support mechanism */
 #define  PCI_EXP_DEVCAP2_OBFF_MSG      0x00040000 /* New message signaling */
 #define PCI_EXP_DEVCTL2                40      /* Device Control 2 */
 #define  PCI_EXP_DEVCTL2_COMP_TIMEOUT  0x000f  /* Completion Timeout Value */
 #define  PCI_EXP_DEVCTL2_ARI           0x0020  /* Alternative Routing-ID */
+#define PCI_EXP_DEVCTL2_ATOMIC_REQ     0x0040  /* Set Atomic requests */
 #define  PCI_EXP_DEVCTL2_IDO_REQ_EN    0x0100  /* Allow IDO for requests */
 #define  PCI_EXP_DEVCTL2_IDO_CMP_EN    0x0200  /* Allow IDO for completions */
 #define  PCI_EXP_DEVCTL2_LTR_EN                0x0400  /* Enable LTR mechanism */
diff --git a/include/uapi/rdma/qedr-abi.h b/include/uapi/rdma/qedr-abi.h
new file mode 100644 (file)
index 0000000..75c270d
--- /dev/null
@@ -0,0 +1,106 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed 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, or the
+ * OpenIB.org BSD license below:
+ *
+ *     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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __QEDR_USER_H__
+#define __QEDR_USER_H__
+
+#include <linux/types.h>
+
+#define QEDR_ABI_VERSION               (8)
+
+/* user kernel communication data structures. */
+
+struct qedr_alloc_ucontext_resp {
+       __u64 db_pa;
+       __u32 db_size;
+
+       __u32 max_send_wr;
+       __u32 max_recv_wr;
+       __u32 max_srq_wr;
+       __u32 sges_per_send_wr;
+       __u32 sges_per_recv_wr;
+       __u32 sges_per_srq_wr;
+       __u32 max_cqes;
+};
+
+struct qedr_alloc_pd_ureq {
+       __u64 rsvd1;
+};
+
+struct qedr_alloc_pd_uresp {
+       __u32 pd_id;
+};
+
+struct qedr_create_cq_ureq {
+       __u64 addr;
+       __u64 len;
+};
+
+struct qedr_create_cq_uresp {
+       __u32 db_offset;
+       __u16 icid;
+};
+
+struct qedr_create_qp_ureq {
+       __u32 qp_handle_hi;
+       __u32 qp_handle_lo;
+
+       /* SQ */
+       /* user space virtual address of SQ buffer */
+       __u64 sq_addr;
+
+       /* length of SQ buffer */
+       __u64 sq_len;
+
+       /* RQ */
+       /* user space virtual address of RQ buffer */
+       __u64 rq_addr;
+
+       /* length of RQ buffer */
+       __u64 rq_len;
+};
+
+struct qedr_create_qp_uresp {
+       __u32 qp_id;
+       __u32 atomic_supported;
+
+       /* SQ */
+       __u32 sq_db_offset;
+       __u16 sq_icid;
+
+       /* RQ */
+       __u32 rq_db_offset;
+       __u16 rq_icid;
+
+       __u32 rq_db2_offset;
+};
+
+#endif /* __QEDR_USER_H__ */
diff --git a/include/video/exynos_mipi_dsim.h b/include/video/exynos_mipi_dsim.h
deleted file mode 100644 (file)
index 6a578f8..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-/* include/video/exynos_mipi_dsim.h
- *
- * Platform data header for Samsung SoC MIPI-DSIM.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.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 _EXYNOS_MIPI_DSIM_H
-#define _EXYNOS_MIPI_DSIM_H
-
-#include <linux/device.h>
-#include <linux/fb.h>
-
-#define PANEL_NAME_SIZE                (32)
-
-/*
- * Enumerate display interface type.
- *
- * DSIM_COMMAND means cpu interface and rgb interface for DSIM_VIDEO.
- *
- * P.S. MIPI DSI Master has two display controller intefaces, RGB Interface
- *     for main display and CPU Interface(same as I80 Interface) for main
- *     and sub display.
- */
-enum mipi_dsim_interface_type {
-       DSIM_COMMAND,
-       DSIM_VIDEO
-};
-
-enum mipi_dsim_virtual_ch_no {
-       DSIM_VIRTUAL_CH_0,
-       DSIM_VIRTUAL_CH_1,
-       DSIM_VIRTUAL_CH_2,
-       DSIM_VIRTUAL_CH_3
-};
-
-enum mipi_dsim_burst_mode_type {
-       DSIM_NON_BURST_SYNC_EVENT,
-       DSIM_BURST_SYNC_EVENT,
-       DSIM_NON_BURST_SYNC_PULSE,
-       DSIM_BURST,
-       DSIM_NON_VIDEO_MODE
-};
-
-enum mipi_dsim_no_of_data_lane {
-       DSIM_DATA_LANE_1,
-       DSIM_DATA_LANE_2,
-       DSIM_DATA_LANE_3,
-       DSIM_DATA_LANE_4
-};
-
-enum mipi_dsim_byte_clk_src {
-       DSIM_PLL_OUT_DIV8,
-       DSIM_EXT_CLK_DIV8,
-       DSIM_EXT_CLK_BYPASS
-};
-
-enum mipi_dsim_pixel_format {
-       DSIM_CMD_3BPP,
-       DSIM_CMD_8BPP,
-       DSIM_CMD_12BPP,
-       DSIM_CMD_16BPP,
-       DSIM_VID_16BPP_565,
-       DSIM_VID_18BPP_666PACKED,
-       DSIM_18BPP_666LOOSELYPACKED,
-       DSIM_24BPP_888
-};
-
-/*
- * struct mipi_dsim_config - interface for configuring mipi-dsi controller.
- *
- * @auto_flush: enable or disable Auto flush of MD FIFO using VSYNC pulse.
- * @eot_disable: enable or disable EoT packet in HS mode.
- * @auto_vertical_cnt: specifies auto vertical count mode.
- *     in Video mode, the vertical line transition uses line counter
- *     configured by VSA, VBP, and Vertical resolution.
- *     If this bit is set to '1', the line counter does not use VSA and VBP
- *     registers.(in command mode, this variable is ignored)
- * @hse: set horizontal sync event mode.
- *     In VSYNC pulse and Vporch area, MIPI DSI master transfers only HSYNC
- *     start packet to MIPI DSI slave at MIPI DSI spec1.1r02.
- *     this bit transfers HSYNC end packet in VSYNC pulse and Vporch area
- *     (in mommand mode, this variable is ignored)
- * @hfp: specifies HFP disable mode.
- *     if this variable is set, DSI master ignores HFP area in VIDEO mode.
- *     (in command mode, this variable is ignored)
- * @hbp: specifies HBP disable mode.
- *     if this variable is set, DSI master ignores HBP area in VIDEO mode.
- *     (in command mode, this variable is ignored)
- * @hsa: specifies HSA disable mode.
- *     if this variable is set, DSI master ignores HSA area in VIDEO mode.
- *     (in command mode, this variable is ignored)
- * @cma_allow: specifies the number of horizontal lines, where command packet
- *     transmission is allowed after Stable VFP period.
- * @e_interface: specifies interface to be used.(CPU or RGB interface)
- * @e_virtual_ch: specifies virtual channel number that main or
- *     sub diaplsy uses.
- * @e_pixel_format: specifies pixel stream format for main or sub display.
- * @e_burst_mode: selects Burst mode in Video mode.
- *     in Non-burst mode, RGB data area is filled with RGB data and NULL
- *     packets, according to input bandwidth of RGB interface.
- *     In Burst mode, RGB data area is filled with RGB data only.
- * @e_no_data_lane: specifies data lane count to be used by Master.
- * @e_byte_clk: select byte clock source. (it must be DSIM_PLL_OUT_DIV8)
- *     DSIM_EXT_CLK_DIV8 and DSIM_EXT_CLK_BYPASSS are not supported.
- * @pll_stable_time: specifies the PLL Timer for stability of the ganerated
- *     clock(System clock cycle base)
- *     if the timer value goes to 0x00000000, the clock stable bit of status
- *     and interrupt register is set.
- * @esc_clk: specifies escape clock frequency for getting the escape clock
- *     prescaler value.
- * @stop_holding_cnt: specifies the interval value between transmitting
- *     read packet(or write "set_tear_on" command) and BTA request.
- *     after transmitting read packet or write "set_tear_on" command,
- *     BTA requests to D-PHY automatically. this counter value specifies
- *     the interval between them.
- * @bta_timeout: specifies the timer for BTA.
- *     this register specifies time out from BTA request to change
- *     the direction with respect to Tx escape clock.
- * @rx_timeout: specifies the timer for LP Rx mode timeout.
- *     this register specifies time out on how long RxValid deasserts,
- *     after RxLpdt asserts with respect to Tx escape clock.
- *     - RxValid specifies Rx data valid indicator.
- *     - RxLpdt specifies an indicator that D-PHY is under RxLpdt mode.
- *     - RxValid and RxLpdt specifies signal from D-PHY.
- */
-struct mipi_dsim_config {
-       unsigned char                   auto_flush;
-       unsigned char                   eot_disable;
-
-       unsigned char                   auto_vertical_cnt;
-       unsigned char                   hse;
-       unsigned char                   hfp;
-       unsigned char                   hbp;
-       unsigned char                   hsa;
-       unsigned char                   cmd_allow;
-
-       enum mipi_dsim_interface_type   e_interface;
-       enum mipi_dsim_virtual_ch_no    e_virtual_ch;
-       enum mipi_dsim_pixel_format     e_pixel_format;
-       enum mipi_dsim_burst_mode_type  e_burst_mode;
-       enum mipi_dsim_no_of_data_lane  e_no_data_lane;
-       enum mipi_dsim_byte_clk_src     e_byte_clk;
-
-       /*
-        * ===========================================
-        * |    P    |    M    |    S    |    MHz    |
-        * -------------------------------------------
-        * |    3    |   100   |    3    |    100    |
-        * |    3    |   100   |    2    |    200    |
-        * |    3    |    63   |    1    |    252    |
-        * |    4    |   100   |    1    |    300    |
-        * |    4    |   110   |    1    |    330    |
-        * |   12    |   350   |    1    |    350    |
-        * |    3    |   100   |    1    |    400    |
-        * |    4    |   150   |    1    |    450    |
-        * |    6    |   118   |    1    |    472    |
-        * |    3    |   120   |    1    |    480    |
-        * |   12    |   250   |    0    |    500    |
-        * |    4    |   100   |    0    |    600    |
-        * |    3    |    81   |    0    |    648    |
-        * |    3    |    88   |    0    |    704    |
-        * |    3    |    90   |    0    |    720    |
-        * |    3    |   100   |    0    |    800    |
-        * |   12    |   425   |    0    |    850    |
-        * |    4    |   150   |    0    |    900    |
-        * |   12    |   475   |    0    |    950    |
-        * |    6    |   250   |    0    |   1000    |
-        * -------------------------------------------
-        */
-
-       /*
-        * pms could be calculated as the following.
-        * M * 24 / P * 2 ^ S = MHz
-        */
-       unsigned char                   p;
-       unsigned short                  m;
-       unsigned char                   s;
-
-       unsigned int                    pll_stable_time;
-       unsigned long                   esc_clk;
-
-       unsigned short                  stop_holding_cnt;
-       unsigned char                   bta_timeout;
-       unsigned short                  rx_timeout;
-};
-
-/*
- * struct mipi_dsim_device - global interface for mipi-dsi driver.
- *
- * @dev: driver model representation of the device.
- * @id: unique device id.
- * @clock: pointer to MIPI-DSI clock of clock framework.
- * @irq: interrupt number to MIPI-DSI controller.
- * @reg_base: base address to memory mapped SRF of MIPI-DSI controller.
- *     (virtual address)
- * @lock: the mutex protecting this data structure.
- * @dsim_info: infomation for configuring mipi-dsi controller.
- * @master_ops: callbacks to mipi-dsi operations.
- * @dsim_lcd_dev: pointer to activated ddi device.
- *     (it would be registered by mipi-dsi driver.)
- * @dsim_lcd_drv: pointer to activated_ddi driver.
- *     (it would be registered by mipi-dsi driver.)
- * @lcd_info: pointer to mipi_lcd_info structure.
- * @state: specifies status of MIPI-DSI controller.
- *     the status could be RESET, INIT, STOP, HSCLKEN and ULPS.
- * @data_lane: specifiec enabled data lane number.
- *     this variable would be set by driver according to e_no_data_lane
- *     automatically.
- * @e_clk_src: select byte clock source.
- * @pd: pointer to MIPI-DSI driver platform data.
- * @phy: pointer to the MIPI-DSI PHY
- */
-struct mipi_dsim_device {
-       struct device                   *dev;
-       int                             id;
-       struct clk                      *clock;
-       unsigned int                    irq;
-       void __iomem                    *reg_base;
-       struct mutex                    lock;
-
-       struct mipi_dsim_config         *dsim_config;
-       struct mipi_dsim_master_ops     *master_ops;
-       struct mipi_dsim_lcd_device     *dsim_lcd_dev;
-       struct mipi_dsim_lcd_driver     *dsim_lcd_drv;
-
-       unsigned int                    state;
-       unsigned int                    data_lane;
-       unsigned int                    e_clk_src;
-       bool                            suspended;
-
-       struct mipi_dsim_platform_data  *pd;
-       struct phy                      *phy;
-};
-
-/*
- * struct mipi_dsim_platform_data - interface to platform data
- *     for mipi-dsi driver.
- *
- * @lcd_panel_name: specifies lcd panel name registered to mipi-dsi driver.
- *     lcd panel driver searched would be actived.
- * @dsim_config: pointer of structure for configuring mipi-dsi controller.
- * @enabled: indicate whether mipi controller got enabled or not.
- * @lcd_panel_info: pointer for lcd panel specific structure.
- *     this structure specifies width, height, timing and polarity and so on.
- */
-struct mipi_dsim_platform_data {
-       char                            lcd_panel_name[PANEL_NAME_SIZE];
-
-       struct mipi_dsim_config         *dsim_config;
-       unsigned int                    enabled;
-       void                            *lcd_panel_info;
-};
-
-/*
- * struct mipi_dsim_master_ops - callbacks to mipi-dsi operations.
- *
- * @cmd_write: transfer command to lcd panel at LP mode.
- * @cmd_read: read command from rx register.
- * @get_dsim_frame_done: get the status that all screen data have been
- *     transferred to mipi-dsi.
- * @clear_dsim_frame_done: clear frame done status.
- * @get_fb_frame_done: get frame done status of display controller.
- * @trigger: trigger display controller.
- *     - this one would be used only in case of CPU mode.
- *  @set_early_blank_mode: set framebuffer blank mode.
- *     - this callback should be called prior to fb_blank() by a client driver
- *     only if needing.
- *  @set_blank_mode: set framebuffer blank mode.
- *     - this callback should be called after fb_blank() by a client driver
- *     only if needing.
- */
-
-struct mipi_dsim_master_ops {
-       int (*cmd_write)(struct mipi_dsim_device *dsim, unsigned int data_id,
-               const unsigned char *data0, unsigned int data1);
-       int (*cmd_read)(struct mipi_dsim_device *dsim, unsigned int data_id,
-               unsigned int data0, unsigned int req_size, u8 *rx_buf);
-       int (*get_dsim_frame_done)(struct mipi_dsim_device *dsim);
-       int (*clear_dsim_frame_done)(struct mipi_dsim_device *dsim);
-
-       int (*get_fb_frame_done)(struct fb_info *info);
-       void (*trigger)(struct fb_info *info);
-       int (*set_early_blank_mode)(struct mipi_dsim_device *dsim, int power);
-       int (*set_blank_mode)(struct mipi_dsim_device *dsim, int power);
-};
-
-/*
- * device structure for mipi-dsi based lcd panel.
- *
- * @name: name of the device to use with this device, or an
- *     alias for that name.
- * @dev: driver model representation of the device.
- * @id: id of device to be registered.
- * @bus_id: bus id for identifing connected bus
- *     and this bus id should be same as id of mipi_dsim_device.
- * @irq: irq number for signaling when framebuffer transfer of
- *     lcd panel module is completed.
- *     this irq would be used only for MIPI-DSI based CPU mode lcd panel.
- * @master: pointer to mipi-dsi master device object.
- * @platform_data: lcd panel specific platform data.
- */
-struct mipi_dsim_lcd_device {
-       char                    *name;
-       struct device           dev;
-       int                     id;
-       int                     bus_id;
-       int                     irq;
-       int                     panel_reverse;
-
-       struct mipi_dsim_device *master;
-       void                    *platform_data;
-};
-
-/*
- * driver structure for mipi-dsi based lcd panel.
- *
- * this structure should be registered by lcd panel driver.
- * mipi-dsi driver seeks lcd panel registered through name field
- * and calls these callback functions in appropriate time.
- *
- * @name: name of the driver to use with this device, or an
- *     alias for that name.
- * @id: id of driver to be registered.
- *     this id would be used for finding device object registered.
- */
-struct mipi_dsim_lcd_driver {
-       char                    *name;
-       int                     id;
-
-       void    (*power_on)(struct mipi_dsim_lcd_device *dsim_dev, int enable);
-       void    (*set_sequence)(struct mipi_dsim_lcd_device *dsim_dev);
-       int     (*probe)(struct mipi_dsim_lcd_device *dsim_dev);
-       int     (*remove)(struct mipi_dsim_lcd_device *dsim_dev);
-       void    (*shutdown)(struct mipi_dsim_lcd_device *dsim_dev);
-       int     (*suspend)(struct mipi_dsim_lcd_device *dsim_dev);
-       int     (*resume)(struct mipi_dsim_lcd_device *dsim_dev);
-};
-
-/*
- * register mipi_dsim_lcd_device to mipi-dsi master.
- */
-int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device
-                                               *lcd_dev);
-/**
- * register mipi_dsim_lcd_driver object defined by lcd panel driver
- * to mipi-dsi driver.
- */
-int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver
-                                               *lcd_drv);
-#endif /* _EXYNOS_MIPI_DSIM_H */
index 7bc47ee31c369d442676edba32233fbb2ddbb462..c4fb45525d08802e721fa8c2c8d67dbb2b296ecf 100644 (file)
@@ -2,6 +2,8 @@
 # Makefile for the linux kernel.
 #
 
+ccflags-y := -fno-function-sections -fno-data-sections
+
 obj-y                          := main.o version.o mounts.o
 ifneq ($(CONFIG_BLK_DEV_INITRD),y)
 obj-y                          += noinitramfs.o
index a8a58e2794a50469d465fad1a6939a4b68ec1114..2858be732f6d25dd8431cd994645c8c6af3828c2 100644 (file)
@@ -789,6 +789,7 @@ int __init_or_module do_one_initcall(initcall_t fn)
        }
        WARN(msgbuf[0], "initcall %pF returned with %s\n", fn, msgbuf);
 
+       add_latent_entropy();
        return ret;
 }
 
index 44066158f0d1fa4e6a81d87e355845ff00b2e4bb..85bc9beb046d9a6deda2e3564f4d5bd01d6fc27b 100644 (file)
@@ -64,6 +64,9 @@
 #include <linux/file.h>
 #include <net/sock.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/cgroup.h>
+
 /*
  * pidlists linger the following amount before being destroyed.  The goal
  * is avoiding frequent destruction in the middle of consecutive read calls
@@ -1176,6 +1179,8 @@ static void cgroup_destroy_root(struct cgroup_root *root)
        struct cgroup *cgrp = &root->cgrp;
        struct cgrp_cset_link *link, *tmp_link;
 
+       trace_cgroup_destroy_root(root);
+
        cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
 
        BUG_ON(atomic_read(&root->nr_cgrps));
@@ -1874,6 +1879,9 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data)
                strcpy(root->release_agent_path, opts.release_agent);
                spin_unlock(&release_agent_path_lock);
        }
+
+       trace_cgroup_remount(root);
+
  out_unlock:
        kfree(opts.release_agent);
        kfree(opts.name);
@@ -2031,6 +2039,8 @@ static int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask)
        if (ret)
                goto destroy_root;
 
+       trace_cgroup_setup_root(root);
+
        /*
         * There must be no failure case after here, since rebinding takes
         * care of subsystems' refcounts, which are explicitly dropped in
@@ -2315,22 +2325,18 @@ static struct file_system_type cgroup2_fs_type = {
        .fs_flags = FS_USERNS_MOUNT,
 };
 
-static char *cgroup_path_ns_locked(struct cgroup *cgrp, char *buf, size_t buflen,
-                                  struct cgroup_namespace *ns)
+static int cgroup_path_ns_locked(struct cgroup *cgrp, char *buf, size_t buflen,
+                                struct cgroup_namespace *ns)
 {
        struct cgroup *root = cset_cgroup_from_root(ns->root_cset, cgrp->root);
-       int ret;
 
-       ret = kernfs_path_from_node(cgrp->kn, root->kn, buf, buflen);
-       if (ret < 0 || ret >= buflen)
-               return NULL;
-       return buf;
+       return kernfs_path_from_node(cgrp->kn, root->kn, buf, buflen);
 }
 
-char *cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
-                    struct cgroup_namespace *ns)
+int cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
+                  struct cgroup_namespace *ns)
 {
-       char *ret;
+       int ret;
 
        mutex_lock(&cgroup_mutex);
        spin_lock_irq(&css_set_lock);
@@ -2357,12 +2363,12 @@ EXPORT_SYMBOL_GPL(cgroup_path_ns);
  *
  * Return value is the same as kernfs_path().
  */
-char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
+int task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
 {
        struct cgroup_root *root;
        struct cgroup *cgrp;
        int hierarchy_id = 1;
-       char *path = NULL;
+       int ret;
 
        mutex_lock(&cgroup_mutex);
        spin_lock_irq(&css_set_lock);
@@ -2371,16 +2377,15 @@ char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
 
        if (root) {
                cgrp = task_cgroup_from_root(task, root);
-               path = cgroup_path_ns_locked(cgrp, buf, buflen, &init_cgroup_ns);
+               ret = cgroup_path_ns_locked(cgrp, buf, buflen, &init_cgroup_ns);
        } else {
                /* if no hierarchy exists, everyone is in "/" */
-               if (strlcpy(buf, "/", buflen) < buflen)
-                       path = buf;
+               ret = strlcpy(buf, "/", buflen);
        }
 
        spin_unlock_irq(&css_set_lock);
        mutex_unlock(&cgroup_mutex);
-       return path;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(task_cgroup_path);
 
@@ -2830,6 +2835,10 @@ static int cgroup_attach_task(struct cgroup *dst_cgrp,
                ret = cgroup_migrate(leader, threadgroup, dst_cgrp->root);
 
        cgroup_migrate_finish(&preloaded_csets);
+
+       if (!ret)
+               trace_cgroup_attach_task(dst_cgrp, leader, threadgroup);
+
        return ret;
 }
 
@@ -3611,6 +3620,8 @@ static int cgroup_rename(struct kernfs_node *kn, struct kernfs_node *new_parent,
        mutex_lock(&cgroup_mutex);
 
        ret = kernfs_rename(kn, new_parent, new_name_str);
+       if (!ret)
+               trace_cgroup_rename(cgrp);
 
        mutex_unlock(&cgroup_mutex);
 
@@ -4381,6 +4392,8 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
 
                if (task) {
                        ret = cgroup_migrate(task, false, to->root);
+                       if (!ret)
+                               trace_cgroup_transfer_tasks(to, task, false);
                        put_task_struct(task);
                }
        } while (task && !ret);
@@ -5046,6 +5059,8 @@ static void css_release_work_fn(struct work_struct *work)
                        ss->css_released(css);
        } else {
                /* cgroup release path */
+               trace_cgroup_release(cgrp);
+
                cgroup_idr_remove(&cgrp->root->cgroup_idr, cgrp->id);
                cgrp->id = -1;
 
@@ -5332,6 +5347,8 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
        if (ret)
                goto out_destroy;
 
+       trace_cgroup_mkdir(cgrp);
+
        /* let's create and online css's */
        kernfs_activate(kn);
 
@@ -5507,6 +5524,9 @@ static int cgroup_rmdir(struct kernfs_node *kn)
 
        ret = cgroup_destroy_locked(cgrp);
 
+       if (!ret)
+               trace_cgroup_rmdir(cgrp);
+
        cgroup_kn_unlock(kn);
        return ret;
 }
@@ -5743,7 +5763,7 @@ core_initcall(cgroup_wq_init);
 int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
                     struct pid *pid, struct task_struct *tsk)
 {
-       char *buf, *path;
+       char *buf;
        int retval;
        struct cgroup_root *root;
 
@@ -5786,18 +5806,18 @@ int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
                 * " (deleted)" is appended to the cgroup path.
                 */
                if (cgroup_on_dfl(cgrp) || !(tsk->flags & PF_EXITING)) {
-                       path = cgroup_path_ns_locked(cgrp, buf, PATH_MAX,
+                       retval = cgroup_path_ns_locked(cgrp, buf, PATH_MAX,
                                                current->nsproxy->cgroup_ns);
-                       if (!path) {
+                       if (retval >= PATH_MAX)
                                retval = -ENAMETOOLONG;
+                       if (retval < 0)
                                goto out_unlock;
-                       }
+
+                       seq_puts(m, buf);
                } else {
-                       path = "/";
+                       seq_puts(m, "/");
                }
 
-               seq_puts(m, path);
-
                if (cgroup_on_dfl(cgrp) && cgroup_is_dead(cgrp))
                        seq_puts(m, " (deleted)\n");
                else
@@ -6062,8 +6082,9 @@ static void cgroup_release_agent(struct work_struct *work)
 {
        struct cgroup *cgrp =
                container_of(work, struct cgroup, release_agent_work);
-       char *pathbuf = NULL, *agentbuf = NULL, *path;
+       char *pathbuf = NULL, *agentbuf = NULL;
        char *argv[3], *envp[3];
+       int ret;
 
        mutex_lock(&cgroup_mutex);
 
@@ -6073,13 +6094,13 @@ static void cgroup_release_agent(struct work_struct *work)
                goto out;
 
        spin_lock_irq(&css_set_lock);
-       path = cgroup_path_ns_locked(cgrp, pathbuf, PATH_MAX, &init_cgroup_ns);
+       ret = cgroup_path_ns_locked(cgrp, pathbuf, PATH_MAX, &init_cgroup_ns);
        spin_unlock_irq(&css_set_lock);
-       if (!path)
+       if (ret < 0 || ret >= PATH_MAX)
                goto out;
 
        argv[0] = agentbuf;
-       argv[1] = path;
+       argv[1] = pathbuf;
        argv[2] = NULL;
 
        /* minimal command environment */
index 5df20d6d152071b40244fb5d85279b8040a641ba..29de1a9352c005c0d4808d2f842c15d29df1b2f8 100644 (file)
@@ -228,7 +228,7 @@ static struct {
        .wq = __WAIT_QUEUE_HEAD_INITIALIZER(cpu_hotplug.wq),
        .lock = __MUTEX_INITIALIZER(cpu_hotplug.lock),
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
-       .dep_map = {.name = "cpu_hotplug.lock" },
+       .dep_map = STATIC_LOCKDEP_MAP_INIT("cpu_hotplug.dep_map", &cpu_hotplug.dep_map),
 #endif
 };
 
index 2b4c20ab5bbe170b8762b783e61df37a38368477..29f815d2ef7e3cfdffe03dd3d9adc409749c61fc 100644 (file)
@@ -2715,7 +2715,7 @@ void __cpuset_memory_pressure_bump(void)
 int proc_cpuset_show(struct seq_file *m, struct pid_namespace *ns,
                     struct pid *pid, struct task_struct *tsk)
 {
-       char *buf, *p;
+       char *buf;
        struct cgroup_subsys_state *css;
        int retval;
 
@@ -2724,14 +2724,15 @@ int proc_cpuset_show(struct seq_file *m, struct pid_namespace *ns,
        if (!buf)
                goto out;
 
-       retval = -ENAMETOOLONG;
        css = task_get_css(tsk, cpuset_cgrp_id);
-       p = cgroup_path_ns(css->cgroup, buf, PATH_MAX,
-                          current->nsproxy->cgroup_ns);
+       retval = cgroup_path_ns(css->cgroup, buf, PATH_MAX,
+                               current->nsproxy->cgroup_ns);
        css_put(css);
-       if (!p)
+       if (retval >= PATH_MAX)
+               retval = -ENAMETOOLONG;
+       if (retval < 0)
                goto out_free;
-       seq_puts(m, p);
+       seq_puts(m, buf);
        seq_putc(m, '\n');
        retval = 0;
 out_free:
index d4129bb05e5d044101ba2cbea672f96954b69a51..f9ec9add21647fb4b60c3be08f515112b4bf4a25 100644 (file)
@@ -300,7 +300,8 @@ int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr,
 
 retry:
        /* Read the page with vaddr into memory */
-       ret = get_user_pages_remote(NULL, mm, vaddr, 1, 0, 1, &old_page, &vma);
+       ret = get_user_pages_remote(NULL, mm, vaddr, 1, FOLL_FORCE, &old_page,
+                       &vma);
        if (ret <= 0)
                return ret;
 
@@ -1710,7 +1711,8 @@ static int is_trap_at_addr(struct mm_struct *mm, unsigned long vaddr)
         * but we treat this as a 'remote' access since it is
         * essentially a kernel access to the memory.
         */
-       result = get_user_pages_remote(NULL, mm, vaddr, 1, 0, 1, &page, NULL);
+       result = get_user_pages_remote(NULL, mm, vaddr, 1, FOLL_FORCE, &page,
+                       NULL);
        if (result < 0)
                return result;
 
index 6d42242485cb2863e940ae0d54983a2d34789096..623259fc794d034f7b4ab9144e2a61a7233381b6 100644 (file)
@@ -547,7 +547,8 @@ free_tsk:
 }
 
 #ifdef CONFIG_MMU
-static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
+static __latent_entropy int dup_mmap(struct mm_struct *mm,
+                                       struct mm_struct *oldmm)
 {
        struct vm_area_struct *mpnt, *tmp, *prev, **pprev;
        struct rb_node **rb_link, *rb_parent;
@@ -1441,7 +1442,8 @@ init_task_pid(struct task_struct *task, enum pid_type type, struct pid *pid)
  * parts of the process environment (as per the clone
  * flags). The actual kick-off is left to the caller.
  */
-static struct task_struct *copy_process(unsigned long clone_flags,
+static __latent_entropy struct task_struct *copy_process(
+                                       unsigned long clone_flags,
                                        unsigned long stack_start,
                                        unsigned long stack_size,
                                        int __user *child_tidptr,
@@ -1926,6 +1928,7 @@ long _do_fork(unsigned long clone_flags,
 
        p = copy_process(clone_flags, stack_start, stack_size,
                         child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
+       add_latent_entropy();
        /*
         * Do this prior waking up the new thread - the thread pointer
         * might get invalid after that point, if the thread exits quickly.
index d5e3973154739f9453926f28e71ec7e20ba753bb..de08fc90baafaf86d3053831d2cc0acd5cb9828e 100644 (file)
@@ -1769,6 +1769,10 @@ static size_t log_output(int facility, int level, enum log_flags lflags, const c
                cont_flush();
        }
 
+       /* Skip empty continuation lines that couldn't be added - they just flush */
+       if (!text_len && (lflags & LOG_CONT))
+               return 0;
+
        /* If it doesn't end in a newline, try to buffer the current line */
        if (!(lflags & LOG_NEWLINE)) {
                if (cont_add(facility, level, lflags, text, text_len))
index 2a99027312a6af6773027e20029752efddc418e3..e6474f7272ec2ce96c95532ea906da113acd5354 100644 (file)
@@ -537,7 +537,7 @@ int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst
                int this_len, retval;
 
                this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
-               retval = access_process_vm(tsk, src, buf, this_len, 0);
+               retval = access_process_vm(tsk, src, buf, this_len, FOLL_FORCE);
                if (!retval) {
                        if (copied)
                                break;
@@ -564,7 +564,8 @@ int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long ds
                this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
                if (copy_from_user(buf, src, this_len))
                        return -EFAULT;
-               retval = access_process_vm(tsk, dst, buf, this_len, 1);
+               retval = access_process_vm(tsk, dst, buf, this_len,
+                               FOLL_FORCE | FOLL_WRITE);
                if (!retval) {
                        if (copied)
                                break;
@@ -1127,7 +1128,7 @@ int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr,
        unsigned long tmp;
        int copied;
 
-       copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
+       copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), FOLL_FORCE);
        if (copied != sizeof(tmp))
                return -EIO;
        return put_user(tmp, (unsigned long __user *)data);
@@ -1138,7 +1139,8 @@ int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
 {
        int copied;
 
-       copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
+       copied = access_process_vm(tsk, addr, &data, sizeof(data),
+                       FOLL_FORCE | FOLL_WRITE);
        return (copied == sizeof(data)) ? 0 : -EIO;
 }
 
@@ -1155,7 +1157,8 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
        switch (request) {
        case PTRACE_PEEKTEXT:
        case PTRACE_PEEKDATA:
-               ret = access_process_vm(child, addr, &word, sizeof(word), 0);
+               ret = access_process_vm(child, addr, &word, sizeof(word),
+                               FOLL_FORCE);
                if (ret != sizeof(word))
                        ret = -EIO;
                else
@@ -1164,7 +1167,8 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
 
        case PTRACE_POKETEXT:
        case PTRACE_POKEDATA:
-               ret = access_process_vm(child, addr, &data, sizeof(data), 1);
+               ret = access_process_vm(child, addr, &data, sizeof(data),
+                               FOLL_FORCE | FOLL_WRITE);
                ret = (ret != sizeof(data) ? -EIO : 0);
                break;
 
index 944b1b491ed84b3d2d1cf977a9838a30cf405121..1898559e6b60ddc52884f6977fca21e57c6f1f90 100644 (file)
@@ -170,7 +170,7 @@ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp)
                                      false));
 }
 
-static void rcu_process_callbacks(struct softirq_action *unused)
+static __latent_entropy void rcu_process_callbacks(struct softirq_action *unused)
 {
        __rcu_process_callbacks(&rcu_sched_ctrlblk);
        __rcu_process_callbacks(&rcu_bh_ctrlblk);
index 7e2e03879c2e55bf259f257868b8b457d5353b07..69a5611a7e7c03dcf950d94badfcce3445863440 100644 (file)
@@ -3013,7 +3013,7 @@ __rcu_process_callbacks(struct rcu_state *rsp)
 /*
  * Do RCU core processing for the current CPU.
  */
-static void rcu_process_callbacks(struct softirq_action *unused)
+static __latent_entropy void rcu_process_callbacks(struct softirq_action *unused)
 {
        struct rcu_state *rsp;
 
index 13935886a4711b2efd576f8890e1564f54991653..fa178b62ea79b53e3cbf37d78d65699e145d6b98 100644 (file)
@@ -415,7 +415,8 @@ static char *task_group_path(struct task_group *tg)
        if (autogroup_path(tg, group_path, PATH_MAX))
                return group_path;
 
-       return cgroup_path(tg->css.cgroup, group_path, PATH_MAX);
+       cgroup_path(tg->css.cgroup, group_path, PATH_MAX);
+       return group_path;
 }
 #endif
 
index 502e95a6e9276cd5ca36dfebbe0f8425610b9c33..d941c97dfbc32d1225925cfaf3ec64d2375b5249 100644 (file)
@@ -690,7 +690,14 @@ void init_entity_runnable_average(struct sched_entity *se)
         * will definitely be update (after enqueue).
         */
        sa->period_contrib = 1023;
-       sa->load_avg = scale_load_down(se->load.weight);
+       /*
+        * Tasks are intialized with full load to be seen as heavy tasks until
+        * they get a chance to stabilize to their real load level.
+        * Group entities are intialized with zero load to reflect the fact that
+        * nothing has been attached to the task group yet.
+        */
+       if (entity_is_task(se))
+               sa->load_avg = scale_load_down(se->load.weight);
        sa->load_sum = sa->load_avg * LOAD_AVG_MAX;
        /*
         * At this point, util_avg won't be used in select_task_rq_fair anyway
@@ -5471,13 +5478,18 @@ static inline int select_idle_smt(struct task_struct *p, struct sched_domain *sd
  */
 static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, int target)
 {
-       struct sched_domain *this_sd = rcu_dereference(*this_cpu_ptr(&sd_llc));
-       u64 avg_idle = this_rq()->avg_idle;
-       u64 avg_cost = this_sd->avg_scan_cost;
+       struct sched_domain *this_sd;
+       u64 avg_cost, avg_idle = this_rq()->avg_idle;
        u64 time, cost;
        s64 delta;
        int cpu, wrap;
 
+       this_sd = rcu_dereference(*this_cpu_ptr(&sd_llc));
+       if (!this_sd)
+               return -1;
+
+       avg_cost = this_sd->avg_scan_cost;
+
        /*
         * Due to large variance we need a large fuzz factor; hackbench in
         * particularly is sensitive here.
@@ -8522,7 +8534,7 @@ static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle) { }
  * run_rebalance_domains is triggered when needed from the scheduler tick.
  * Also triggered for nohz idle balancing (with nohz_balancing_kick set).
  */
-static void run_rebalance_domains(struct softirq_action *h)
+static __latent_entropy void run_rebalance_domains(struct softirq_action *h)
 {
        struct rq *this_rq = this_rq();
        enum cpu_idle_type idle = this_rq->idle_balance ?
index 66762645f9e86c30505610736f0380a2cdd1ae00..1bf81ef913755ae797c90e2affc3330f54d3a7df 100644 (file)
@@ -496,7 +496,7 @@ void __tasklet_hi_schedule_first(struct tasklet_struct *t)
 }
 EXPORT_SYMBOL(__tasklet_hi_schedule_first);
 
-static void tasklet_action(struct softirq_action *a)
+static __latent_entropy void tasklet_action(struct softirq_action *a)
 {
        struct tasklet_struct *list;
 
@@ -532,7 +532,7 @@ static void tasklet_action(struct softirq_action *a)
        }
 }
 
-static void tasklet_hi_action(struct softirq_action *a)
+static __latent_entropy void tasklet_hi_action(struct softirq_action *a)
 {
        struct tasklet_struct *list;
 
index c3aad685bbc036cc2f03672085aedd9dc9c8fb86..12dd190634ab3e0617ac260133818523f142f8a2 100644 (file)
@@ -542,7 +542,6 @@ static int alarm_clock_get(clockid_t which_clock, struct timespec *tp)
 static int alarm_timer_create(struct k_itimer *new_timer)
 {
        enum  alarmtimer_type type;
-       struct alarm_base *base;
 
        if (!alarmtimer_get_rtcdev())
                return -ENOTSUPP;
@@ -551,7 +550,6 @@ static int alarm_timer_create(struct k_itimer *new_timer)
                return -EPERM;
 
        type = clock2alarm(new_timer->it_clock);
-       base = &alarm_bases[type];
        alarm_init(&new_timer->it.alarm.alarmtimer, type, alarm_handle_timer);
        return 0;
 }
index 32bf6f75a8fec255c6d5fbf38e9fecd9e1e848fa..2d47980a1bc423df44e8e1324537f4e185b338ae 100644 (file)
@@ -1633,7 +1633,7 @@ static inline void __run_timers(struct timer_base *base)
 /*
  * This function runs timers and the timer-tq in bottom half context.
  */
-static void run_timer_softirq(struct softirq_action *h)
+static __latent_entropy void run_timer_softirq(struct softirq_action *h)
 {
        struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);
 
index 992ab9d99f3576620625d1b0d2011f22e0eadfd4..e5798084554911440844e1757c9ee656dc40cf12 100644 (file)
@@ -1,8 +1,4 @@
 
-# We are fully aware of the dangers of __builtin_return_address()
-FRAME_CFLAGS := $(call cc-disable-warning,frame-address)
-KBUILD_CFLAGS += $(FRAME_CFLAGS)
-
 # Do not instrument the tracer itself:
 
 ifdef CONFIG_FUNCTION_TRACER
index 39d07e754822b94a259554c1e66e2709ecac6631..33bc56cf60d71fc81f5f5981100406e1c7a91b10 100644 (file)
@@ -1857,15 +1857,6 @@ config PROVIDE_OHCI1394_DMA_INIT
 
          See Documentation/debugging-via-ohci1394.txt for more information.
 
-config BUILD_DOCSRC
-       bool "Build targets in Documentation/ tree"
-       depends on HEADERS_CHECK
-       help
-         This option attempts to build objects from the source files in the
-         kernel Documentation/ tree.
-
-         Say N if you are unsure.
-
 config DMA_API_DEBUG
        bool "Enable debugging of DMA-API usage"
        depends on HAVE_DMA_API_DEBUG
index 7312e7784611d7c56b6c154a2c2bb9975142792b..f0c7f1481baeefe30f14820ef9a2782a1541a175 100644 (file)
@@ -1139,6 +1139,28 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags)
 }
 EXPORT_SYMBOL(dup_iter);
 
+/**
+ * import_iovec() - Copy an array of &struct iovec from userspace
+ *     into the kernel, check that it is valid, and initialize a new
+ *     &struct iov_iter iterator to access it.
+ *
+ * @type: One of %READ or %WRITE.
+ * @uvector: Pointer to the userspace array.
+ * @nr_segs: Number of elements in userspace array.
+ * @fast_segs: Number of elements in @iov.
+ * @iov: (input and output parameter) Pointer to pointer to (usually small
+ *     on-stack) kernel array.
+ * @i: Pointer to iterator that will be initialized on success.
+ *
+ * If the array pointed to by *@iov is large enough to hold all @nr_segs,
+ * then this function places %NULL in *@iov on return. Otherwise, a new
+ * array will be allocated and the result placed in *@iov. This means that
+ * the caller may call kfree() on *@iov regardless of whether the small
+ * on-stack array was used or not (and regardless of whether this function
+ * returns an error or not).
+ *
+ * Return: 0 on success or negative error code on error.
+ */
 int import_iovec(int type, const struct iovec __user * uvector,
                 unsigned nr_segs, unsigned fast_segs,
                 struct iovec **iov, struct iov_iter *i)
index 2be55692aa43fc5f9d4f9fbe4d337e38fe359b45..1d6565e810309eb710523a814983849d76101c6b 100644 (file)
@@ -74,7 +74,7 @@ void irq_poll_complete(struct irq_poll *iop)
 }
 EXPORT_SYMBOL(irq_poll_complete);
 
-static void irq_poll_softirq(struct softirq_action *h)
+static void __latent_entropy irq_poll_softirq(struct softirq_action *h)
 {
        struct list_head *list = this_cpu_ptr(&blk_cpu_iopoll);
        int rearm = 0, budget = irq_poll_budget;
index 27fe74948882e9558ad8cc72dfa6da1b7051dce7..9ac959ef4cae972374540f34895465507e656d7b 100644 (file)
@@ -33,6 +33,7 @@
 
 #define PERCPU_COUNT_BIAS      (1LU << (BITS_PER_LONG - 1))
 
+static DEFINE_SPINLOCK(percpu_ref_switch_lock);
 static DECLARE_WAIT_QUEUE_HEAD(percpu_ref_switch_waitq);
 
 static unsigned long __percpu *percpu_count_ptr(struct percpu_ref *ref)
@@ -82,6 +83,7 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release,
        atomic_long_set(&ref->count, start_count);
 
        ref->release = release;
+       ref->confirm_switch = NULL;
        return 0;
 }
 EXPORT_SYMBOL_GPL(percpu_ref_init);
@@ -101,6 +103,8 @@ void percpu_ref_exit(struct percpu_ref *ref)
        unsigned long __percpu *percpu_count = percpu_count_ptr(ref);
 
        if (percpu_count) {
+               /* non-NULL confirm_switch indicates switching in progress */
+               WARN_ON_ONCE(ref->confirm_switch);
                free_percpu(percpu_count);
                ref->percpu_count_ptr = __PERCPU_REF_ATOMIC_DEAD;
        }
@@ -161,66 +165,23 @@ static void percpu_ref_noop_confirm_switch(struct percpu_ref *ref)
 static void __percpu_ref_switch_to_atomic(struct percpu_ref *ref,
                                          percpu_ref_func_t *confirm_switch)
 {
-       if (!(ref->percpu_count_ptr & __PERCPU_REF_ATOMIC)) {
-               /* switching from percpu to atomic */
-               ref->percpu_count_ptr |= __PERCPU_REF_ATOMIC;
-
-               /*
-                * Non-NULL ->confirm_switch is used to indicate that
-                * switching is in progress.  Use noop one if unspecified.
-                */
-               WARN_ON_ONCE(ref->confirm_switch);
-               ref->confirm_switch =
-                       confirm_switch ?: percpu_ref_noop_confirm_switch;
-
-               percpu_ref_get(ref);    /* put after confirmation */
-               call_rcu_sched(&ref->rcu, percpu_ref_switch_to_atomic_rcu);
-       } else if (confirm_switch) {
-               /*
-                * Somebody already set ATOMIC.  Switching may still be in
-                * progress.  @confirm_switch must be invoked after the
-                * switching is complete and a full sched RCU grace period
-                * has passed.  Wait synchronously for the previous
-                * switching and schedule @confirm_switch invocation.
-                */
-               wait_event(percpu_ref_switch_waitq, !ref->confirm_switch);
-               ref->confirm_switch = confirm_switch;
-
-               percpu_ref_get(ref);    /* put after confirmation */
-               call_rcu_sched(&ref->rcu, percpu_ref_call_confirm_rcu);
+       if (ref->percpu_count_ptr & __PERCPU_REF_ATOMIC) {
+               if (confirm_switch)
+                       confirm_switch(ref);
+               return;
        }
-}
 
-/**
- * percpu_ref_switch_to_atomic - switch a percpu_ref to atomic mode
- * @ref: percpu_ref to switch to atomic mode
- * @confirm_switch: optional confirmation callback
- *
- * There's no reason to use this function for the usual reference counting.
- * Use percpu_ref_kill[_and_confirm]().
- *
- * Schedule switching of @ref to atomic mode.  All its percpu counts will
- * be collected to the main atomic counter.  On completion, when all CPUs
- * are guaraneed to be in atomic mode, @confirm_switch, which may not
- * block, is invoked.  This function may be invoked concurrently with all
- * the get/put operations and can safely be mixed with kill and reinit
- * operations.  Note that @ref will stay in atomic mode across kill/reinit
- * cycles until percpu_ref_switch_to_percpu() is called.
- *
- * This function normally doesn't block and can be called from any context
- * but it may block if @confirm_kill is specified and @ref is already in
- * the process of switching to atomic mode.  In such cases, @confirm_switch
- * will be invoked after the switching is complete.
- *
- * Due to the way percpu_ref is implemented, @confirm_switch will be called
- * after at least one full sched RCU grace period has passed but this is an
- * implementation detail and must not be depended upon.
- */
-void percpu_ref_switch_to_atomic(struct percpu_ref *ref,
-                                percpu_ref_func_t *confirm_switch)
-{
-       ref->force_atomic = true;
-       __percpu_ref_switch_to_atomic(ref, confirm_switch);
+       /* switching from percpu to atomic */
+       ref->percpu_count_ptr |= __PERCPU_REF_ATOMIC;
+
+       /*
+        * Non-NULL ->confirm_switch is used to indicate that switching is
+        * in progress.  Use noop one if unspecified.
+        */
+       ref->confirm_switch = confirm_switch ?: percpu_ref_noop_confirm_switch;
+
+       percpu_ref_get(ref);    /* put after confirmation */
+       call_rcu_sched(&ref->rcu, percpu_ref_switch_to_atomic_rcu);
 }
 
 static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
@@ -233,8 +194,6 @@ static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
        if (!(ref->percpu_count_ptr & __PERCPU_REF_ATOMIC))
                return;
 
-       wait_event(percpu_ref_switch_waitq, !ref->confirm_switch);
-
        atomic_long_add(PERCPU_COUNT_BIAS, &ref->count);
 
        /*
@@ -250,6 +209,58 @@ static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
                          ref->percpu_count_ptr & ~__PERCPU_REF_ATOMIC);
 }
 
+static void __percpu_ref_switch_mode(struct percpu_ref *ref,
+                                    percpu_ref_func_t *confirm_switch)
+{
+       lockdep_assert_held(&percpu_ref_switch_lock);
+
+       /*
+        * If the previous ATOMIC switching hasn't finished yet, wait for
+        * its completion.  If the caller ensures that ATOMIC switching
+        * isn't in progress, this function can be called from any context.
+        */
+       wait_event_lock_irq(percpu_ref_switch_waitq, !ref->confirm_switch,
+                           percpu_ref_switch_lock);
+
+       if (ref->force_atomic || (ref->percpu_count_ptr & __PERCPU_REF_DEAD))
+               __percpu_ref_switch_to_atomic(ref, confirm_switch);
+       else
+               __percpu_ref_switch_to_percpu(ref);
+}
+
+/**
+ * percpu_ref_switch_to_atomic - switch a percpu_ref to atomic mode
+ * @ref: percpu_ref to switch to atomic mode
+ * @confirm_switch: optional confirmation callback
+ *
+ * There's no reason to use this function for the usual reference counting.
+ * Use percpu_ref_kill[_and_confirm]().
+ *
+ * Schedule switching of @ref to atomic mode.  All its percpu counts will
+ * be collected to the main atomic counter.  On completion, when all CPUs
+ * are guaraneed to be in atomic mode, @confirm_switch, which may not
+ * block, is invoked.  This function may be invoked concurrently with all
+ * the get/put operations and can safely be mixed with kill and reinit
+ * operations.  Note that @ref will stay in atomic mode across kill/reinit
+ * cycles until percpu_ref_switch_to_percpu() is called.
+ *
+ * This function may block if @ref is in the process of switching to atomic
+ * mode.  If the caller ensures that @ref is not in the process of
+ * switching to atomic mode, this function can be called from any context.
+ */
+void percpu_ref_switch_to_atomic(struct percpu_ref *ref,
+                                percpu_ref_func_t *confirm_switch)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&percpu_ref_switch_lock, flags);
+
+       ref->force_atomic = true;
+       __percpu_ref_switch_mode(ref, confirm_switch);
+
+       spin_unlock_irqrestore(&percpu_ref_switch_lock, flags);
+}
+
 /**
  * percpu_ref_switch_to_percpu - switch a percpu_ref to percpu mode
  * @ref: percpu_ref to switch to percpu mode
@@ -264,17 +275,20 @@ static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
  * dying or dead, the actual switching takes place on the following
  * percpu_ref_reinit().
  *
- * This function normally doesn't block and can be called from any context
- * but it may block if @ref is in the process of switching to atomic mode
- * by percpu_ref_switch_atomic().
+ * This function may block if @ref is in the process of switching to atomic
+ * mode.  If the caller ensures that @ref is not in the process of
+ * switching to atomic mode, this function can be called from any context.
  */
 void percpu_ref_switch_to_percpu(struct percpu_ref *ref)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&percpu_ref_switch_lock, flags);
+
        ref->force_atomic = false;
+       __percpu_ref_switch_mode(ref, NULL);
 
-       /* a dying or dead ref can't be switched to percpu mode w/o reinit */
-       if (!(ref->percpu_count_ptr & __PERCPU_REF_DEAD))
-               __percpu_ref_switch_to_percpu(ref);
+       spin_unlock_irqrestore(&percpu_ref_switch_lock, flags);
 }
 
 /**
@@ -290,21 +304,23 @@ void percpu_ref_switch_to_percpu(struct percpu_ref *ref)
  *
  * This function normally doesn't block and can be called from any context
  * but it may block if @confirm_kill is specified and @ref is in the
- * process of switching to atomic mode by percpu_ref_switch_atomic().
- *
- * Due to the way percpu_ref is implemented, @confirm_switch will be called
- * after at least one full sched RCU grace period has passed but this is an
- * implementation detail and must not be depended upon.
+ * process of switching to atomic mode by percpu_ref_switch_to_atomic().
  */
 void percpu_ref_kill_and_confirm(struct percpu_ref *ref,
                                 percpu_ref_func_t *confirm_kill)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&percpu_ref_switch_lock, flags);
+
        WARN_ONCE(ref->percpu_count_ptr & __PERCPU_REF_DEAD,
                  "%s called more than once on %pf!", __func__, ref->release);
 
        ref->percpu_count_ptr |= __PERCPU_REF_DEAD;
-       __percpu_ref_switch_to_atomic(ref, confirm_kill);
+       __percpu_ref_switch_mode(ref, confirm_kill);
        percpu_ref_put(ref);
+
+       spin_unlock_irqrestore(&percpu_ref_switch_lock, flags);
 }
 EXPORT_SYMBOL_GPL(percpu_ref_kill_and_confirm);
 
@@ -321,11 +337,16 @@ EXPORT_SYMBOL_GPL(percpu_ref_kill_and_confirm);
  */
 void percpu_ref_reinit(struct percpu_ref *ref)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&percpu_ref_switch_lock, flags);
+
        WARN_ON_ONCE(!percpu_ref_is_zero(ref));
 
        ref->percpu_count_ptr &= ~__PERCPU_REF_DEAD;
        percpu_ref_get(ref);
-       if (!ref->force_atomic)
-               __percpu_ref_switch_to_percpu(ref);
+       __percpu_ref_switch_mode(ref, NULL);
+
+       spin_unlock_irqrestore(&percpu_ref_switch_lock, flags);
 }
 EXPORT_SYMBOL_GPL(percpu_ref_reinit);
index 915982b304bbb16a9d7924b3c1957857189ab6ba..fa594b1140e648899410d15920ea5705d7202661 100644 (file)
@@ -47,7 +47,7 @@ static inline void prandom_state_selftest(void)
 }
 #endif
 
-static DEFINE_PER_CPU(struct rnd_state, net_rand_state);
+static DEFINE_PER_CPU(struct rnd_state, net_rand_state) __latent_entropy;
 
 /**
  *     prandom_u32_state - seeded pseudo-random number generator.
index 2ca1faf3fa09038feaeea4fb4adbe5ea6717df30..295bd7a9f76bbb292d68f088546dc220231458d4 100644 (file)
@@ -21,9 +21,6 @@ KCOV_INSTRUMENT_memcontrol.o := n
 KCOV_INSTRUMENT_mmzone.o := n
 KCOV_INSTRUMENT_vmstat.o := n
 
-# Since __builtin_frame_address does work as used, disable the warning.
-CFLAGS_usercopy.o += $(call cc-disable-warning, frame-address)
-
 mmu-y                  := nommu.o
 mmu-$(CONFIG_MMU)      := gup.o highmem.o memory.o mincore.o \
                           mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \
index 381bb07ed14f2271e487fb98d0e95f6ff5470750..db77dcb38afda3d3720a228e14236fb2e324f929 100644 (file)
  * get_vaddr_frames() - map virtual addresses to pfns
  * @start:     starting user address
  * @nr_frames: number of pages / pfns from start to map
- * @write:     whether pages will be written to by the caller
- * @force:     whether to force write access even if user mapping is
- *             readonly. See description of the same argument of
-               get_user_pages().
+ * @gup_flags: flags modifying lookup behaviour
  * @vec:       structure which receives pages / pfns of the addresses mapped.
  *             It should have space for at least nr_frames entries.
  *
@@ -34,7 +31,7 @@
  * This function takes care of grabbing mmap_sem as necessary.
  */
 int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
-                    bool write, bool force, struct frame_vector *vec)
+                    unsigned int gup_flags, struct frame_vector *vec)
 {
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma;
@@ -59,7 +56,7 @@ int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
                vec->got_ref = true;
                vec->is_pfns = false;
                ret = get_user_pages_locked(start, nr_frames,
-                       write, force, (struct page **)(vec->ptrs), &locked);
+                       gup_flags, (struct page **)(vec->ptrs), &locked);
                goto out;
        }
 
index 96b2b2fd0fbd13f0b7385e7adc3359c6c7793503..7aa113c2d3731fb253689bca2a54260affdab7ec 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -60,6 +60,16 @@ static int follow_pfn_pte(struct vm_area_struct *vma, unsigned long address,
        return -EEXIST;
 }
 
+/*
+ * FOLL_FORCE can write to even unwritable pte's, but only
+ * after we've gone through a COW cycle and they are dirty.
+ */
+static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)
+{
+       return pte_write(pte) ||
+               ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte));
+}
+
 static struct page *follow_page_pte(struct vm_area_struct *vma,
                unsigned long address, pmd_t *pmd, unsigned int flags)
 {
@@ -95,7 +105,7 @@ retry:
        }
        if ((flags & FOLL_NUMA) && pte_protnone(pte))
                goto no_page;
-       if ((flags & FOLL_WRITE) && !pte_write(pte)) {
+       if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) {
                pte_unmap_unlock(ptep, ptl);
                return NULL;
        }
@@ -412,7 +422,7 @@ static int faultin_page(struct task_struct *tsk, struct vm_area_struct *vma,
         * reCOWed by userspace write).
         */
        if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
-               *flags &= ~FOLL_WRITE;
+               *flags |= FOLL_COW;
        return 0;
 }
 
@@ -729,7 +739,6 @@ static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
                                                struct mm_struct *mm,
                                                unsigned long start,
                                                unsigned long nr_pages,
-                                               int write, int force,
                                                struct page **pages,
                                                struct vm_area_struct **vmas,
                                                int *locked, bool notify_drop,
@@ -747,10 +756,6 @@ static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
 
        if (pages)
                flags |= FOLL_GET;
-       if (write)
-               flags |= FOLL_WRITE;
-       if (force)
-               flags |= FOLL_FORCE;
 
        pages_done = 0;
        lock_dropped = false;
@@ -843,12 +848,12 @@ static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
  *          up_read(&mm->mmap_sem);
  */
 long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
-                          int write, int force, struct page **pages,
+                          unsigned int gup_flags, struct page **pages,
                           int *locked)
 {
        return __get_user_pages_locked(current, current->mm, start, nr_pages,
-                                      write, force, pages, NULL, locked, true,
-                                      FOLL_TOUCH);
+                                      pages, NULL, locked, true,
+                                      gup_flags | FOLL_TOUCH);
 }
 EXPORT_SYMBOL(get_user_pages_locked);
 
@@ -864,14 +869,14 @@ EXPORT_SYMBOL(get_user_pages_locked);
  */
 __always_inline long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
                                               unsigned long start, unsigned long nr_pages,
-                                              int write, int force, struct page **pages,
-                                              unsigned int gup_flags)
+                                              struct page **pages, unsigned int gup_flags)
 {
        long ret;
        int locked = 1;
+
        down_read(&mm->mmap_sem);
-       ret = __get_user_pages_locked(tsk, mm, start, nr_pages, write, force,
-                                     pages, NULL, &locked, false, gup_flags);
+       ret = __get_user_pages_locked(tsk, mm, start, nr_pages, pages, NULL,
+                                     &locked, false, gup_flags);
        if (locked)
                up_read(&mm->mmap_sem);
        return ret;
@@ -896,10 +901,10 @@ EXPORT_SYMBOL(__get_user_pages_unlocked);
  * "force" parameter).
  */
 long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
-                            int write, int force, struct page **pages)
+                            struct page **pages, unsigned int gup_flags)
 {
        return __get_user_pages_unlocked(current, current->mm, start, nr_pages,
-                                        write, force, pages, FOLL_TOUCH);
+                                        pages, gup_flags | FOLL_TOUCH);
 }
 EXPORT_SYMBOL(get_user_pages_unlocked);
 
@@ -910,9 +915,7 @@ EXPORT_SYMBOL(get_user_pages_unlocked);
  * @mm:                mm_struct of target mm
  * @start:     starting user address
  * @nr_pages:  number of pages from start to pin
- * @write:     whether pages will be written to by the caller
- * @force:     whether to force access even when user mapping is currently
- *             protected (but never forces write access to shared mapping).
+ * @gup_flags: flags modifying lookup behaviour
  * @pages:     array that receives pointers to the pages pinned.
  *             Should be at least nr_pages long. Or NULL, if caller
  *             only intends to ensure the pages are faulted in.
@@ -941,9 +944,9 @@ EXPORT_SYMBOL(get_user_pages_unlocked);
  * or similar operation cannot guarantee anything stronger anyway because
  * locks can't be held over the syscall boundary.
  *
- * If write=0, the page must not be written to. If the page is written to,
- * set_page_dirty (or set_page_dirty_lock, as appropriate) must be called
- * after the page is finished with, and before put_page is called.
+ * If gup_flags & FOLL_WRITE == 0, the page must not be written to. If the page
+ * is written to, set_page_dirty (or set_page_dirty_lock, as appropriate) must
+ * be called after the page is finished with, and before put_page is called.
  *
  * get_user_pages is typically used for fewer-copy IO operations, to get a
  * handle on the memory by some means other than accesses via the user virtual
@@ -960,12 +963,12 @@ EXPORT_SYMBOL(get_user_pages_unlocked);
  */
 long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
                unsigned long start, unsigned long nr_pages,
-               int write, int force, struct page **pages,
+               unsigned int gup_flags, struct page **pages,
                struct vm_area_struct **vmas)
 {
-       return __get_user_pages_locked(tsk, mm, start, nr_pages, write, force,
-                                      pages, vmas, NULL, false,
-                                      FOLL_TOUCH | FOLL_REMOTE);
+       return __get_user_pages_locked(tsk, mm, start, nr_pages, pages, vmas,
+                                      NULL, false,
+                                      gup_flags | FOLL_TOUCH | FOLL_REMOTE);
 }
 EXPORT_SYMBOL(get_user_pages_remote);
 
@@ -976,12 +979,12 @@ EXPORT_SYMBOL(get_user_pages_remote);
  * obviously don't pass FOLL_REMOTE in here.
  */
 long get_user_pages(unsigned long start, unsigned long nr_pages,
-               int write, int force, struct page **pages,
+               unsigned int gup_flags, struct page **pages,
                struct vm_area_struct **vmas)
 {
        return __get_user_pages_locked(current, current->mm, start, nr_pages,
-                                      write, force, pages, vmas, NULL, false,
-                                      FOLL_TOUCH);
+                                      pages, vmas, NULL, false,
+                                      gup_flags | FOLL_TOUCH);
 }
 EXPORT_SYMBOL(get_user_pages);
 
@@ -1505,7 +1508,8 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
                start += nr << PAGE_SHIFT;
                pages += nr;
 
-               ret = get_user_pages_unlocked(start, nr_pages - nr, write, 0, pages);
+               ret = get_user_pages_unlocked(start, nr_pages - nr, pages,
+                               write ? FOLL_WRITE : 0);
 
                /* Have to be a bit careful with return values */
                if (nr > 0) {
index 88af13c00d3cbfedb1d6d42ef5ddcf6ca9a50cab..70c009741aab705c6bb344df1f1851d9582bc7a7 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/vmalloc.h>
+#include <linux/bug.h>
 
 #include "kasan.h"
 #include "../slab.h"
@@ -62,7 +63,7 @@ void kasan_unpoison_shadow(const void *address, size_t size)
        }
 }
 
-static void __kasan_unpoison_stack(struct task_struct *task, void *sp)
+static void __kasan_unpoison_stack(struct task_struct *task, const void *sp)
 {
        void *base = task_stack_page(task);
        size_t size = sp - base;
@@ -77,9 +78,24 @@ void kasan_unpoison_task_stack(struct task_struct *task)
 }
 
 /* Unpoison the stack for the current task beyond a watermark sp value. */
-asmlinkage void kasan_unpoison_remaining_stack(void *sp)
+asmlinkage void kasan_unpoison_task_stack_below(const void *watermark)
 {
-       __kasan_unpoison_stack(current, sp);
+       __kasan_unpoison_stack(current, watermark);
+}
+
+/*
+ * Clear all poison for the region between the current SP and a provided
+ * watermark value, as is sometimes required prior to hand-crafted asm function
+ * returns in the middle of functions.
+ */
+void kasan_unpoison_stack_above_sp_to(const void *watermark)
+{
+       const void *sp = __builtin_frame_address(0);
+       size_t size = watermark - sp;
+
+       if (WARN_ON(sp > watermark))
+               return;
+       kasan_unpoison_shadow(sp, size);
 }
 
 /*
index fc1987dfd8cc7f62fa911fd905f743cb3ec53ec3..e18c57bdc75c4c96e3ef79546afe11fab5a3c07a 100644 (file)
@@ -3869,10 +3869,11 @@ EXPORT_SYMBOL_GPL(generic_access_phys);
  * given task for page fault accounting.
  */
 static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
-               unsigned long addr, void *buf, int len, int write)
+               unsigned long addr, void *buf, int len, unsigned int gup_flags)
 {
        struct vm_area_struct *vma;
        void *old_buf = buf;
+       int write = gup_flags & FOLL_WRITE;
 
        down_read(&mm->mmap_sem);
        /* ignore errors, just check how much was successfully transferred */
@@ -3882,7 +3883,7 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
                struct page *page = NULL;
 
                ret = get_user_pages_remote(tsk, mm, addr, 1,
-                               write, 1, &page, &vma);
+                               gup_flags, &page, &vma);
                if (ret <= 0) {
 #ifndef CONFIG_HAVE_IOREMAP_PROT
                        break;
@@ -3934,14 +3935,14 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
  * @addr:      start address to access
  * @buf:       source or destination buffer
  * @len:       number of bytes to transfer
- * @write:     whether the access is a write
+ * @gup_flags: flags modifying lookup behaviour
  *
  * The caller must hold a reference on @mm.
  */
 int access_remote_vm(struct mm_struct *mm, unsigned long addr,
-               void *buf, int len, int write)
+               void *buf, int len, unsigned int gup_flags)
 {
-       return __access_remote_vm(NULL, mm, addr, buf, len, write);
+       return __access_remote_vm(NULL, mm, addr, buf, len, gup_flags);
 }
 
 /*
@@ -3950,7 +3951,7 @@ int access_remote_vm(struct mm_struct *mm, unsigned long addr,
  * Do not walk the page table directly, use get_user_pages
  */
 int access_process_vm(struct task_struct *tsk, unsigned long addr,
-               void *buf, int len, int write)
+               void *buf, int len, unsigned int gup_flags)
 {
        struct mm_struct *mm;
        int ret;
@@ -3959,7 +3960,8 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr,
        if (!mm)
                return 0;
 
-       ret = __access_remote_vm(tsk, mm, addr, buf, len, write);
+       ret = __access_remote_vm(tsk, mm, addr, buf, len, gup_flags);
+
        mmput(mm);
 
        return ret;
index ad1c96ac313c0f442b1635baaa96555d355a74bb..0b859af06b87df4e17af4180b953a6337c251dff 100644 (file)
@@ -850,7 +850,7 @@ static int lookup_node(unsigned long addr)
        struct page *p;
        int err;
 
-       err = get_user_pages(addr & PAGE_MASK, 1, 0, 0, &p, NULL);
+       err = get_user_pages(addr & PAGE_MASK, 1, 0, &p, NULL);
        if (err >= 0) {
                err = page_to_nid(p);
                put_page(p);
index 95daf81a4855d99d2ae176ab16c92a1b4302b0ae..db5fd1795298764d06abc9a6fdbc1657cc3dcd2b 100644 (file)
@@ -160,33 +160,25 @@ finish_or_fault:
  * - don't permit access to VMAs that don't support it, such as I/O mappings
  */
 long get_user_pages(unsigned long start, unsigned long nr_pages,
-                   int write, int force, struct page **pages,
+                   unsigned int gup_flags, struct page **pages,
                    struct vm_area_struct **vmas)
 {
-       int flags = 0;
-
-       if (write)
-               flags |= FOLL_WRITE;
-       if (force)
-               flags |= FOLL_FORCE;
-
-       return __get_user_pages(current, current->mm, start, nr_pages, flags,
-                               pages, vmas, NULL);
+       return __get_user_pages(current, current->mm, start, nr_pages,
+                               gup_flags, pages, vmas, NULL);
 }
 EXPORT_SYMBOL(get_user_pages);
 
 long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
-                           int write, int force, struct page **pages,
+                           unsigned int gup_flags, struct page **pages,
                            int *locked)
 {
-       return get_user_pages(start, nr_pages, write, force, pages, NULL);
+       return get_user_pages(start, nr_pages, gup_flags, pages, NULL);
 }
 EXPORT_SYMBOL(get_user_pages_locked);
 
 long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
                               unsigned long start, unsigned long nr_pages,
-                              int write, int force, struct page **pages,
-                              unsigned int gup_flags)
+                              struct page **pages, unsigned int gup_flags)
 {
        long ret;
        down_read(&mm->mmap_sem);
@@ -198,10 +190,10 @@ long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
 EXPORT_SYMBOL(__get_user_pages_unlocked);
 
 long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
-                            int write, int force, struct page **pages)
+                            struct page **pages, unsigned int gup_flags)
 {
        return __get_user_pages_unlocked(current, current->mm, start, nr_pages,
-                                        write, force, pages, 0);
+                                        pages, gup_flags);
 }
 EXPORT_SYMBOL(get_user_pages_unlocked);
 
@@ -1817,9 +1809,10 @@ void filemap_map_pages(struct fault_env *fe,
 EXPORT_SYMBOL(filemap_map_pages);
 
 static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
-               unsigned long addr, void *buf, int len, int write)
+               unsigned long addr, void *buf, int len, unsigned int gup_flags)
 {
        struct vm_area_struct *vma;
+       int write = gup_flags & FOLL_WRITE;
 
        down_read(&mm->mmap_sem);
 
@@ -1854,21 +1847,22 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
  * @addr:      start address to access
  * @buf:       source or destination buffer
  * @len:       number of bytes to transfer
- * @write:     whether the access is a write
+ * @gup_flags: flags modifying lookup behaviour
  *
  * The caller must hold a reference on @mm.
  */
 int access_remote_vm(struct mm_struct *mm, unsigned long addr,
-               void *buf, int len, int write)
+               void *buf, int len, unsigned int gup_flags)
 {
-       return __access_remote_vm(NULL, mm, addr, buf, len, write);
+       return __access_remote_vm(NULL, mm, addr, buf, len, gup_flags);
 }
 
 /*
  * Access another process' address space.
  * - source/target buffer must be kernel space
  */
-int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write)
+int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len,
+               unsigned int gup_flags)
 {
        struct mm_struct *mm;
 
@@ -1879,7 +1873,7 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in
        if (!mm)
                return 0;
 
-       len = __access_remote_vm(tsk, mm, addr, buf, len, write);
+       len = __access_remote_vm(tsk, mm, addr, buf, len, gup_flags);
 
        mmput(mm);
        return len;
index ca423cc20b5985b9ba5528e7648881f05445a50d..2b3bf6767d5441a876890dce16d9de2d60f1e2bc 100644 (file)
@@ -91,6 +91,11 @@ EXPORT_PER_CPU_SYMBOL(_numa_mem_);
 int _node_numa_mem_[MAX_NUMNODES];
 #endif
 
+#ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY
+volatile u64 latent_entropy __latent_entropy;
+EXPORT_SYMBOL(latent_entropy);
+#endif
+
 /*
  * Array of node states.
  */
index 9903830aaebbf00256fbb108891c2e5e09baecac..255714302394137e37e4d8bd5cf87d685732d9d6 100644 (file)
@@ -1961,8 +1961,9 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
        void *base = (void *)ULONG_MAX;
        void **areas = NULL;
        struct pcpu_alloc_info *ai;
-       size_t size_sum, areas_size, max_distance;
-       int group, i, rc;
+       size_t size_sum, areas_size;
+       unsigned long max_distance;
+       int group, i, highest_group, rc;
 
        ai = pcpu_build_alloc_info(reserved_size, dyn_size, atom_size,
                                   cpu_distance_fn);
@@ -1978,7 +1979,8 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
                goto out_free;
        }
 
-       /* allocate, copy and determine base address */
+       /* allocate, copy and determine base address & max_distance */
+       highest_group = 0;
        for (group = 0; group < ai->nr_groups; group++) {
                struct pcpu_group_info *gi = &ai->groups[group];
                unsigned int cpu = NR_CPUS;
@@ -1999,6 +2001,21 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
                areas[group] = ptr;
 
                base = min(ptr, base);
+               if (ptr > areas[highest_group])
+                       highest_group = group;
+       }
+       max_distance = areas[highest_group] - base;
+       max_distance += ai->unit_size * ai->groups[highest_group].nr_units;
+
+       /* warn if maximum distance is further than 75% of vmalloc space */
+       if (max_distance > VMALLOC_TOTAL * 3 / 4) {
+               pr_warn("max_distance=0x%lx too large for vmalloc space 0x%lx\n",
+                               max_distance, VMALLOC_TOTAL);
+#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
+               /* and fail if we have fallback */
+               rc = -EINVAL;
+               goto out_free_areas;
+#endif
        }
 
        /*
@@ -2023,23 +2040,8 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
        }
 
        /* base address is now known, determine group base offsets */
-       max_distance = 0;
        for (group = 0; group < ai->nr_groups; group++) {
                ai->groups[group].base_offset = areas[group] - base;
-               max_distance = max_t(size_t, max_distance,
-                                    ai->groups[group].base_offset);
-       }
-       max_distance += ai->unit_size;
-
-       /* warn if maximum distance is further than 75% of vmalloc space */
-       if (max_distance > VMALLOC_TOTAL * 3 / 4) {
-               pr_warn("max_distance=0x%zx too large for vmalloc space 0x%lx\n",
-                       max_distance, VMALLOC_TOTAL);
-#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
-               /* and fail if we have fallback */
-               rc = -EINVAL;
-               goto out_free;
-#endif
        }
 
        pr_info("Embedded %zu pages/cpu @%p s%zu r%zu d%zu u%zu\n",
index 07514d41ebcc1623b789fc93e09794058ecdc6ca..be8dc8d1edb95b34d8c6b7fbf34321e597e981e1 100644 (file)
@@ -88,12 +88,16 @@ static int process_vm_rw_single_vec(unsigned long addr,
        ssize_t rc = 0;
        unsigned long max_pages_per_loop = PVM_MAX_KMALLOC_PAGES
                / sizeof(struct pages *);
+       unsigned int flags = FOLL_REMOTE;
 
        /* Work out address and page range required */
        if (len == 0)
                return 0;
        nr_pages = (addr + len - 1) / PAGE_SIZE - addr / PAGE_SIZE + 1;
 
+       if (vm_write)
+               flags |= FOLL_WRITE;
+
        while (!rc && nr_pages && iov_iter_count(iter)) {
                int pages = min(nr_pages, max_pages_per_loop);
                size_t bytes;
@@ -104,8 +108,7 @@ static int process_vm_rw_single_vec(unsigned long addr,
                 * current/current->mm
                 */
                pages = __get_user_pages_unlocked(task, mm, pa, pages,
-                                                 vm_write, 0, process_pages,
-                                                 FOLL_REMOTE);
+                                                 process_pages, flags);
                if (pages <= 0)
                        return -EFAULT;
 
index 662cddf914af2048ab6c9c674f59d37a7aceba69..952cbe7ad7b75183d54df0925826f892f5e4f536 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -283,7 +283,8 @@ EXPORT_SYMBOL_GPL(__get_user_pages_fast);
 int __weak get_user_pages_fast(unsigned long start,
                                int nr_pages, int write, struct page **pages)
 {
-       return get_user_pages_unlocked(start, nr_pages, write, 0, pages);
+       return get_user_pages_unlocked(start, nr_pages, pages,
+                                      write ? FOLL_WRITE : 0);
 }
 EXPORT_SYMBOL_GPL(get_user_pages_fast);
 
@@ -623,7 +624,7 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen)
        if (len > buflen)
                len = buflen;
 
-       res = access_process_vm(task, arg_start, buffer, len, 0);
+       res = access_process_vm(task, arg_start, buffer, len, FOLL_FORCE);
 
        /*
         * If the nul at the end of args has been overwritten, then
@@ -638,7 +639,8 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen)
                        if (len > buflen - res)
                                len = buflen - res;
                        res += access_process_vm(task, env_start,
-                                                buffer+res, len, 0);
+                                                buffer+res, len,
+                                                FOLL_FORCE);
                        res = strnlen(buffer, res);
                }
        }
index e657258e1f2cd0f6d43084cc447e384a6850f290..8bd569695e76fb76112bd5c17fb1a8027c6590a5 100644 (file)
@@ -217,6 +217,7 @@ static const struct brport_attribute *brport_attrs[] = {
 #endif
        &brport_attr_proxyarp,
        &brport_attr_proxyarp_wifi,
+       &brport_attr_multicast_flood,
        NULL
 };
 
index 00d2601407c5dfd5b8e8a94bf5de2160ae29cc38..1a7c9a79a53c22e8e61e3b6e8e1720db71d9f9a9 100644 (file)
@@ -26,7 +26,7 @@ struct page **ceph_get_direct_page_vector(const void __user *data,
        while (got < num_pages) {
                rc = get_user_pages_unlocked(
                    (unsigned long)data + ((unsigned long)got * PAGE_SIZE),
-                   num_pages - got, write_page, 0, pages + got);
+                   num_pages - got, pages + got, write_page ? FOLL_WRITE : 0);
                if (rc < 0)
                        break;
                BUG_ON(rc == 0);
index f1fe26f6645883a54126300adc030f1c3a04652d..4bc19a164ba5dc1dbf5d0f3f378f61e49d3be9dd 100644 (file)
@@ -3845,7 +3845,7 @@ int netif_rx_ni(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(netif_rx_ni);
 
-static void net_tx_action(struct softirq_action *h)
+static __latent_entropy void net_tx_action(struct softirq_action *h)
 {
        struct softnet_data *sd = this_cpu_ptr(&softnet_data);
 
@@ -5198,7 +5198,7 @@ out_unlock:
        return work;
 }
 
-static void net_rx_action(struct softirq_action *h)
+static __latent_entropy void net_rx_action(struct softirq_action *h)
 {
        struct softnet_data *sd = this_cpu_ptr(&softnet_data);
        unsigned long time_limit = jiffies + 2;
index b06d2f46b83e98c4fb6a92cefd625c2918eafdeb..fb7348f135014fcea21b621ab30e1ad02b62448d 100644 (file)
@@ -1144,6 +1144,8 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
        if (dev->netdev_ops->ndo_get_vf_config(dev, vfs_num, &ivi))
                return 0;
 
+       memset(&vf_vlan_info, 0, sizeof(vf_vlan_info));
+
        vf_mac.vf =
                vf_vlan.vf =
                vf_vlan_info.vf =
index f2be689a6c85b6fd7619d3449db69756aa2e9aba..62d4d90c1389c4ea7da37c81779b2f55207d2a92 100644 (file)
@@ -2265,7 +2265,8 @@ struct rtable *__ip_route_output_key_hash(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.
 
index 54cf7197c7ab52c082c88bce10bbd22a31a57a59..5a27ab4eab3974280aad827241944b53daab569e 100644 (file)
@@ -1190,6 +1190,16 @@ out:
        return NULL;
 }
 
+static void tcp_v6_restore_cb(struct sk_buff *skb)
+{
+       /* We need to move header back to the beginning if xfrm6_policy_check()
+        * and tcp_v6_fill_cb() are going to be called again.
+        * ip6_datagram_recv_specific_ctl() also expects IP6CB to be there.
+        */
+       memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
+               sizeof(struct inet6_skb_parm));
+}
+
 /* The socket must have it's spinlock held when we get
  * here, unless it is a TCP_LISTEN socket.
  *
@@ -1319,6 +1329,7 @@ ipv6_pktoptions:
                        np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb));
                if (ipv6_opt_accepted(sk, opt_skb, &TCP_SKB_CB(opt_skb)->header.h6)) {
                        skb_set_owner_r(opt_skb, sk);
+                       tcp_v6_restore_cb(opt_skb);
                        opt_skb = xchg(&np->pktoptions, opt_skb);
                } else {
                        __kfree_skb(opt_skb);
@@ -1352,15 +1363,6 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr,
        TCP_SKB_CB(skb)->sacked = 0;
 }
 
-static void tcp_v6_restore_cb(struct sk_buff *skb)
-{
-       /* We need to move header back to the beginning if xfrm6_policy_check()
-        * and tcp_v6_fill_cb() are going to be called again.
-        */
-       memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
-               sizeof(struct inet6_skb_parm));
-}
-
 static int tcp_v6_rcv(struct sk_buff *skb)
 {
        const struct tcphdr *th;
index c8c82e109c6893c4dc1a8f5bbff72849b7cab205..22087062bd1013e7c526dc95bce379bcaaebc4b9 100644 (file)
@@ -343,7 +343,7 @@ static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
        key->eth.cvlan.tci = 0;
        key->eth.cvlan.tpid = 0;
 
-       if (likely(skb_vlan_tag_present(skb))) {
+       if (skb_vlan_tag_present(skb)) {
                key->eth.vlan.tci = htons(skb->vlan_tci);
                key->eth.vlan.tpid = skb->vlan_proto;
        } else {
index 95c36147a6e136b1e11db6bbbb673c4b7f248e1a..e7da29021b38b7a81555d784473f88b7d6e3759d 100644 (file)
@@ -176,7 +176,7 @@ static void do_setup(struct net_device *netdev)
 
        netdev->vlan_features = netdev->features;
        netdev->hw_enc_features = netdev->features;
-       netdev->features |= NETIF_F_HW_VLAN_CTAG_TX;
+       netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
        netdev->hw_features = netdev->features & ~NETIF_F_LLTX;
 
        eth_hw_addr_random(netdev);
index 8f198437c724839e61a84cc71fd1f4090912a9fc..7387418ac514ae0ab679aa1b0d677ddbc9363f7b 100644 (file)
@@ -485,7 +485,8 @@ static unsigned int packet_length(const struct sk_buff *skb)
 {
        unsigned int length = skb->len - ETH_HLEN;
 
-       if (skb_vlan_tagged(skb))
+       if (!skb_vlan_tag_present(skb) &&
+           eth_type_vlan(skb->protocol))
                length -= VLAN_HLEN;
 
        /* Don't subtract for multiple VLAN tags. Most (all?) drivers allow
index c9102172ce3b0fa72ceabacc8475ae30298d0ec4..a512b18c0088506bc577d8b3a1113871206c6d47 100644 (file)
@@ -341,22 +341,25 @@ int tcf_register_action(struct tc_action_ops *act,
        if (!act->act || !act->dump || !act->init || !act->walk || !act->lookup)
                return -EINVAL;
 
+       /* We have to register pernet ops before making the action ops visible,
+        * otherwise tcf_action_init_1() could get a partially initialized
+        * netns.
+        */
+       ret = register_pernet_subsys(ops);
+       if (ret)
+               return ret;
+
        write_lock(&act_mod_lock);
        list_for_each_entry(a, &act_base, head) {
                if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
                        write_unlock(&act_mod_lock);
+                       unregister_pernet_subsys(ops);
                        return -EEXIST;
                }
        }
        list_add_tail(&act->head, &act_base);
        write_unlock(&act_mod_lock);
 
-       ret = register_pernet_subsys(ops);
-       if (ret) {
-               tcf_unregister_action(act, ops);
-               return ret;
-       }
-
        return 0;
 }
 EXPORT_SYMBOL(tcf_register_action);
@@ -367,8 +370,6 @@ int tcf_unregister_action(struct tc_action_ops *act,
        struct tc_action_ops *a;
        int err = -ENOENT;
 
-       unregister_pernet_subsys(ops);
-
        write_lock(&act_mod_lock);
        list_for_each_entry(a, &act_base, head) {
                if (a == act) {
@@ -378,6 +379,8 @@ int tcf_unregister_action(struct tc_action_ops *act,
                }
        }
        write_unlock(&act_mod_lock);
+       if (!err)
+               unregister_pernet_subsys(ops);
        return err;
 }
 EXPORT_SYMBOL(tcf_unregister_action);
index 11da7da0b7c42520ae89d065156aeee7eed0f380..2ee29a3375f6672812e45e12250ec90ac1ed892c 100644 (file)
@@ -101,7 +101,7 @@ EXPORT_SYMBOL(unregister_tcf_proto_ops);
 
 static int tfilter_notify(struct net *net, struct sk_buff *oskb,
                          struct nlmsghdr *n, struct tcf_proto *tp,
-                         unsigned long fh, int event);
+                         unsigned long fh, int event, bool unicast);
 
 static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
                                 struct nlmsghdr *n,
@@ -112,7 +112,7 @@ static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
 
        for (it_chain = chain; (tp = rtnl_dereference(*it_chain)) != NULL;
             it_chain = &tp->next)
-               tfilter_notify(net, oskb, n, tp, 0, event);
+               tfilter_notify(net, oskb, n, tp, 0, event, false);
 }
 
 /* Select new prio value from the range, managed by kernel. */
@@ -319,7 +319,8 @@ replay:
 
                        RCU_INIT_POINTER(*back, next);
 
-                       tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
+                       tfilter_notify(net, skb, n, tp, fh,
+                                      RTM_DELTFILTER, false);
                        tcf_destroy(tp, true);
                        err = 0;
                        goto errout;
@@ -345,14 +346,14 @@ replay:
                                struct tcf_proto *next = rtnl_dereference(tp->next);
 
                                tfilter_notify(net, skb, n, tp, fh,
-                                              RTM_DELTFILTER);
+                                              RTM_DELTFILTER, false);
                                if (tcf_destroy(tp, false))
                                        RCU_INIT_POINTER(*back, next);
                        }
                        goto errout;
                case RTM_GETTFILTER:
                        err = tfilter_notify(net, skb, n, tp, fh,
-                                            RTM_NEWTFILTER);
+                                            RTM_NEWTFILTER, true);
                        goto errout;
                default:
                        err = -EINVAL;
@@ -367,7 +368,7 @@ replay:
                        RCU_INIT_POINTER(tp->next, rtnl_dereference(*back));
                        rcu_assign_pointer(*back, tp);
                }
-               tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
+               tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false);
        } else {
                if (tp_created)
                        tcf_destroy(tp, true);
@@ -419,7 +420,7 @@ nla_put_failure:
 
 static int tfilter_notify(struct net *net, struct sk_buff *oskb,
                          struct nlmsghdr *n, struct tcf_proto *tp,
-                         unsigned long fh, int event)
+                         unsigned long fh, int event, bool unicast)
 {
        struct sk_buff *skb;
        u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
@@ -433,6 +434,9 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
                return -EINVAL;
        }
 
+       if (unicast)
+               return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
+
        return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
                              n->nlmsg_flags & NLM_F_ECHO);
 }
index 5c7549b5b92cd23c551fc65775d1abbd4aa768cb..41adf362936d7dc4035cf9db7d7962f8dafcad0d 100644 (file)
@@ -246,7 +246,7 @@ static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb,
                                } else {
                                        strp->rx_interrupted = 1;
                                }
-                               strp_parser_err(strp, err, desc);
+                               strp_parser_err(strp, len, desc);
                                break;
                        } else if (len > strp->sk->sk_rcvbuf) {
                                /* Message length exceeds maximum allowed */
index a7e42f9a405c1c6fde97fc3cc34628a45016b03c..2bff63a73cf8a98aab11e3f6cbb2154988907557 100644 (file)
@@ -551,7 +551,7 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
                        *entry, *new;
        unsigned int nr;
 
-       nr = hash_long(from_kuid(&init_user_ns, acred->uid), cache->hashbits);
+       nr = auth->au_ops->hash_cred(acred, cache->hashbits);
 
        rcu_read_lock();
        hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) {
index 83dffeadf20acbec87e93ef96fdad20d80e8f630..f1df9837f1acaf0161d360c6e4f02122dc6ff5d8 100644 (file)
@@ -78,6 +78,14 @@ static struct rpc_cred *generic_bind_cred(struct rpc_task *task,
        return auth->au_ops->lookup_cred(auth, acred, lookupflags);
 }
 
+static int
+generic_hash_cred(struct auth_cred *acred, unsigned int hashbits)
+{
+       return hash_64(from_kgid(&init_user_ns, acred->gid) |
+               ((u64)from_kuid(&init_user_ns, acred->uid) <<
+                       (sizeof(gid_t) * 8)), hashbits);
+}
+
 /*
  * Lookup generic creds for current process
  */
@@ -258,6 +266,7 @@ generic_key_timeout(struct rpc_auth *auth, struct rpc_cred *cred)
 static const struct rpc_authops generic_auth_ops = {
        .owner = THIS_MODULE,
        .au_name = "Generic",
+       .hash_cred = generic_hash_cred,
        .lookup_cred = generic_lookup_cred,
        .crcreate = generic_create_cred,
        .key_timeout = generic_key_timeout,
index 976c7812bbd520e51d34eb542b15f0e4730034b9..d8bd97a5a7c9d807fd478befe92bea4ea7a40407 100644 (file)
@@ -1298,6 +1298,12 @@ gss_destroy_cred(struct rpc_cred *cred)
        gss_destroy_nullcred(cred);
 }
 
+static int
+gss_hash_cred(struct auth_cred *acred, unsigned int hashbits)
+{
+       return hash_64(from_kuid(&init_user_ns, acred->uid), hashbits);
+}
+
 /*
  * Lookup RPCSEC_GSS cred for the current process
  */
@@ -1982,6 +1988,7 @@ static const struct rpc_authops authgss_ops = {
        .au_name        = "RPCSEC_GSS",
        .create         = gss_create,
        .destroy        = gss_destroy,
+       .hash_cred      = gss_hash_cred,
        .lookup_cred    = gss_lookup_cred,
        .crcreate       = gss_create_cred,
        .list_pseudoflavors = gss_mech_list_pseudoflavors,
index a1d768a973f5297a60899d475763d184228010db..306fc0f545967a1643a4b80c6d787637a9950329 100644 (file)
@@ -46,6 +46,14 @@ unx_destroy(struct rpc_auth *auth)
        rpcauth_clear_credcache(auth->au_credcache);
 }
 
+static int
+unx_hash_cred(struct auth_cred *acred, unsigned int hashbits)
+{
+       return hash_64(from_kgid(&init_user_ns, acred->gid) |
+               ((u64)from_kuid(&init_user_ns, acred->uid) <<
+                       (sizeof(gid_t) * 8)), hashbits);
+}
+
 /*
  * Lookup AUTH_UNIX creds for current process
  */
@@ -220,6 +228,7 @@ const struct rpc_authops authunix_ops = {
        .au_name        = "UNIX",
        .create         = unx_create,
        .destroy        = unx_destroy,
+       .hash_cred      = unx_hash_cred,
        .lookup_cred    = unx_lookup_cred,
        .crcreate       = unx_create_cred,
 };
index 229956bf84577f81d380536b76d1e57d16de53ae..ac701c28f44f376195f27507ba1c61698d0f9b65 100644 (file)
@@ -76,13 +76,7 @@ static int xprt_alloc_xdr_buf(struct xdr_buf *buf, gfp_t gfp_flags)
        page = alloc_page(gfp_flags);
        if (page == NULL)
                return -ENOMEM;
-       buf->head[0].iov_base = page_address(page);
-       buf->head[0].iov_len = PAGE_SIZE;
-       buf->tail[0].iov_base = NULL;
-       buf->tail[0].iov_len = 0;
-       buf->page_len = 0;
-       buf->len = 0;
-       buf->buflen = PAGE_SIZE;
+       xdr_buf_init(buf, page_address(page), PAGE_SIZE);
        return 0;
 }
 
index 4d8e11f94a35fb12390c291330c56331512f13d5..8aabe12201f8a069f788b2828735a98fc81c39ef 100644 (file)
@@ -353,7 +353,7 @@ void sunrpc_init_cache_detail(struct cache_detail *cd)
        spin_unlock(&cache_list_lock);
 
        /* start the cleaning process */
-       schedule_delayed_work(&cache_cleaner, 0);
+       queue_delayed_work(system_power_efficient_wq, &cache_cleaner, 0);
 }
 EXPORT_SYMBOL_GPL(sunrpc_init_cache_detail);
 
@@ -476,7 +476,8 @@ static void do_cache_clean(struct work_struct *work)
                delay = 0;
 
        if (delay)
-               schedule_delayed_work(&cache_cleaner, delay);
+               queue_delayed_work(system_power_efficient_wq,
+                                  &cache_cleaner, delay);
 }
 
 
index 66f23b376fa04a91134eddf6d8ee22a2f5de5808..34dd7b26ee5f16589a46f8c7faa158570ccec9ad 100644 (file)
@@ -184,7 +184,6 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
                                   struct super_block *sb)
 {
        struct dentry *dentry;
-       int err = 0;
 
        switch (event) {
        case RPC_PIPEFS_MOUNT:
@@ -201,7 +200,7 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
                printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event);
                return -ENOTSUPP;
        }
-       return err;
+       return 0;
 }
 
 static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event,
@@ -988,7 +987,6 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt)
 {
 
        if (clnt != NULL) {
-               rpc_task_release_client(task);
                if (task->tk_xprt == NULL)
                        task->tk_xprt = xprt_iter_get_next(&clnt->cl_xpi);
                task->tk_client = clnt;
@@ -1693,6 +1691,7 @@ call_allocate(struct rpc_task *task)
        struct rpc_rqst *req = task->tk_rqstp;
        struct rpc_xprt *xprt = req->rq_xprt;
        struct rpc_procinfo *proc = task->tk_msg.rpc_proc;
+       int status;
 
        dprint_status(task);
 
@@ -1718,11 +1717,14 @@ call_allocate(struct rpc_task *task)
        req->rq_rcvsize = RPC_REPHDRSIZE + slack + proc->p_replen;
        req->rq_rcvsize <<= 2;
 
-       req->rq_buffer = xprt->ops->buf_alloc(task,
-                                       req->rq_callsize + req->rq_rcvsize);
-       if (req->rq_buffer != NULL)
-               return;
+       status = xprt->ops->buf_alloc(task);
        xprt_inject_disconnect(xprt);
+       if (status == 0)
+               return;
+       if (status != -ENOMEM) {
+               rpc_exit(task, status);
+               return;
+       }
 
        dprintk("RPC: %5u rpc_buffer allocation failed\n", task->tk_pid);
 
@@ -1748,18 +1750,6 @@ rpc_task_force_reencode(struct rpc_task *task)
        task->tk_rqstp->rq_bytes_sent = 0;
 }
 
-static inline void
-rpc_xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
-{
-       buf->head[0].iov_base = start;
-       buf->head[0].iov_len = len;
-       buf->tail[0].iov_len = 0;
-       buf->page_len = 0;
-       buf->flags = 0;
-       buf->len = 0;
-       buf->buflen = len;
-}
-
 /*
  * 3.  Encode arguments of an RPC call
  */
@@ -1772,12 +1762,12 @@ rpc_xdr_encode(struct rpc_task *task)
 
        dprint_status(task);
 
-       rpc_xdr_buf_init(&req->rq_snd_buf,
-                        req->rq_buffer,
-                        req->rq_callsize);
-       rpc_xdr_buf_init(&req->rq_rcv_buf,
-                        (char *)req->rq_buffer + req->rq_callsize,
-                        req->rq_rcvsize);
+       xdr_buf_init(&req->rq_snd_buf,
+                    req->rq_buffer,
+                    req->rq_callsize);
+       xdr_buf_init(&req->rq_rcv_buf,
+                    req->rq_rbuffer,
+                    req->rq_rcvsize);
 
        p = rpc_encode_header(task);
        if (p == NULL) {
@@ -2615,6 +2605,70 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt,
 }
 EXPORT_SYMBOL_GPL(rpc_clnt_test_and_add_xprt);
 
+/**
+ * rpc_clnt_setup_test_and_add_xprt()
+ *
+ * This is an rpc_clnt_add_xprt setup() function which returns 1 so:
+ *   1) caller of the test function must dereference the rpc_xprt_switch
+ *   and the rpc_xprt.
+ *   2) test function must call rpc_xprt_switch_add_xprt, usually in
+ *   the rpc_call_done routine.
+ *
+ * Upon success (return of 1), the test function adds the new
+ * transport to the rpc_clnt xprt switch
+ *
+ * @clnt: struct rpc_clnt to get the new transport
+ * @xps:  the rpc_xprt_switch to hold the new transport
+ * @xprt: the rpc_xprt to test
+ * @data: a struct rpc_add_xprt_test pointer that holds the test function
+ *        and test function call data
+ */
+int rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *clnt,
+                                    struct rpc_xprt_switch *xps,
+                                    struct rpc_xprt *xprt,
+                                    void *data)
+{
+       struct rpc_cred *cred;
+       struct rpc_task *task;
+       struct rpc_add_xprt_test *xtest = (struct rpc_add_xprt_test *)data;
+       int status = -EADDRINUSE;
+
+       xprt = xprt_get(xprt);
+       xprt_switch_get(xps);
+
+       if (rpc_xprt_switch_has_addr(xps, (struct sockaddr *)&xprt->addr))
+               goto out_err;
+
+       /* Test the connection */
+       cred = authnull_ops.lookup_cred(NULL, NULL, 0);
+       task = rpc_call_null_helper(clnt, xprt, cred,
+                                   RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
+                                   NULL, NULL);
+       put_rpccred(cred);
+       if (IS_ERR(task)) {
+               status = PTR_ERR(task);
+               goto out_err;
+       }
+       status = task->tk_status;
+       rpc_put_task(task);
+
+       if (status < 0)
+               goto out_err;
+
+       /* rpc_xprt_switch and rpc_xprt are deferrenced by add_xprt_test() */
+       xtest->add_xprt_test(clnt, xprt, xtest->data);
+
+       /* so that rpc_clnt_add_xprt does not call rpc_xprt_switch_add_xprt */
+       return 1;
+out_err:
+       xprt_put(xprt);
+       xprt_switch_put(xps);
+       pr_info("RPC:   rpc_clnt_test_xprt failed: %d addr %s not added\n",
+               status, xprt->address_strings[RPC_DISPLAY_ADDR]);
+       return status;
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_setup_test_and_add_xprt);
+
 /**
  * rpc_clnt_add_xprt - Add a new transport to a rpc_clnt
  * @clnt: pointer to struct rpc_clnt
@@ -2697,6 +2751,34 @@ rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt, unsigned long timeo)
 }
 EXPORT_SYMBOL_GPL(rpc_cap_max_reconnect_timeout);
 
+void rpc_clnt_xprt_switch_put(struct rpc_clnt *clnt)
+{
+       xprt_switch_put(rcu_dereference(clnt->cl_xpi.xpi_xpswitch));
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_put);
+
+void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
+{
+       rpc_xprt_switch_add_xprt(rcu_dereference(clnt->cl_xpi.xpi_xpswitch),
+                                xprt);
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_add_xprt);
+
+bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
+                                  const struct sockaddr *sap)
+{
+       struct rpc_xprt_switch *xps;
+       bool ret;
+
+       xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
+
+       rcu_read_lock();
+       ret = rpc_xprt_switch_has_addr(xps, sap);
+       rcu_read_unlock();
+       return ret;
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_has_addr);
+
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
 static void rpc_show_header(void)
 {
index 9ae588511aafd9470736a4ff24a498c941b7ece3..5db68b371db2cac54e37cc0a883259dbc61125cb 100644 (file)
@@ -849,14 +849,17 @@ static void rpc_async_schedule(struct work_struct *work)
 }
 
 /**
- * rpc_malloc - allocate an RPC buffer
- * @task: RPC task that will use this buffer
- * @size: requested byte size
+ * rpc_malloc - allocate RPC buffer resources
+ * @task: RPC task
+ *
+ * A single memory region is allocated, which is split between the
+ * RPC call and RPC reply that this task is being used for. When
+ * this RPC is retired, the memory is released by calling rpc_free.
  *
  * To prevent rpciod from hanging, this allocator never sleeps,
- * returning NULL and suppressing warning if the request cannot be serviced
- * immediately.
- * The caller can arrange to sleep in a way that is safe for rpciod.
+ * returning -ENOMEM and suppressing warning if the request cannot
+ * be serviced immediately. The caller can arrange to sleep in a
+ * way that is safe for rpciod.
  *
  * Most requests are 'small' (under 2KiB) and can be serviced from a
  * mempool, ensuring that NFS reads and writes can always proceed,
@@ -865,8 +868,10 @@ static void rpc_async_schedule(struct work_struct *work)
  * In order to avoid memory starvation triggering more writebacks of
  * NFS requests, we avoid using GFP_KERNEL.
  */
-void *rpc_malloc(struct rpc_task *task, size_t size)
+int rpc_malloc(struct rpc_task *task)
 {
+       struct rpc_rqst *rqst = task->tk_rqstp;
+       size_t size = rqst->rq_callsize + rqst->rq_rcvsize;
        struct rpc_buffer *buf;
        gfp_t gfp = GFP_NOIO | __GFP_NOWARN;
 
@@ -880,28 +885,28 @@ void *rpc_malloc(struct rpc_task *task, size_t size)
                buf = kmalloc(size, gfp);
 
        if (!buf)
-               return NULL;
+               return -ENOMEM;
 
        buf->len = size;
        dprintk("RPC: %5u allocated buffer of size %zu at %p\n",
                        task->tk_pid, size, buf);
-       return &buf->data;
+       rqst->rq_buffer = buf->data;
+       rqst->rq_rbuffer = (char *)rqst->rq_buffer + rqst->rq_callsize;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(rpc_malloc);
 
 /**
- * rpc_free - free buffer allocated via rpc_malloc
- * @buffer: buffer to free
+ * rpc_free - free RPC buffer resources allocated via rpc_malloc
+ * @task: RPC task
  *
  */
-void rpc_free(void *buffer)
+void rpc_free(struct rpc_task *task)
 {
+       void *buffer = task->tk_rqstp->rq_buffer;
        size_t size;
        struct rpc_buffer *buf;
 
-       if (!buffer)
-               return;
-
        buf = container_of(buffer, struct rpc_buffer, data);
        size = buf->len;
 
index c5b0cb4f4056c4da0a0adc556cf46bebb48d2e7a..7c8070ec93c8a74b9735e7b388c2de404141a0df 100644 (file)
@@ -401,6 +401,21 @@ int svc_bind(struct svc_serv *serv, struct net *net)
 }
 EXPORT_SYMBOL_GPL(svc_bind);
 
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
+static void
+__svc_init_bc(struct svc_serv *serv)
+{
+       INIT_LIST_HEAD(&serv->sv_cb_list);
+       spin_lock_init(&serv->sv_cb_lock);
+       init_waitqueue_head(&serv->sv_cb_waitq);
+}
+#else
+static void
+__svc_init_bc(struct svc_serv *serv)
+{
+}
+#endif
+
 /*
  * Create an RPC service
  */
@@ -443,6 +458,8 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
        init_timer(&serv->sv_temptimer);
        spin_lock_init(&serv->sv_lock);
 
+       __svc_init_bc(serv);
+
        serv->sv_nrpools = npools;
        serv->sv_pools =
                kcalloc(serv->sv_nrpools, sizeof(struct svc_pool),
index c4f3cc0c07752e3fbbe8e72316f2203a065e1646..7f1071e103cafd7e4fadd75969cad8d5235fa927 100644 (file)
@@ -767,7 +767,7 @@ static void xdr_set_next_page(struct xdr_stream *xdr)
        newbase -= xdr->buf->page_base;
 
        if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0)
-               xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len);
+               xdr_set_iov(xdr, xdr->buf->tail, xdr->nwords << 2);
 }
 
 static bool xdr_set_next_buffer(struct xdr_stream *xdr)
@@ -776,7 +776,7 @@ static bool xdr_set_next_buffer(struct xdr_stream *xdr)
                xdr_set_next_page(xdr);
        else if (xdr->iov == xdr->buf->head) {
                if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0)
-                       xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len);
+                       xdr_set_iov(xdr, xdr->buf->tail, xdr->nwords << 2);
        }
        return xdr->p != xdr->end;
 }
@@ -859,12 +859,15 @@ EXPORT_SYMBOL_GPL(xdr_set_scratch_buffer);
 static __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes)
 {
        __be32 *p;
-       void *cpdest = xdr->scratch.iov_base;
+       char *cpdest = xdr->scratch.iov_base;
        size_t cplen = (char *)xdr->end - (char *)xdr->p;
 
        if (nbytes > xdr->scratch.iov_len)
                return NULL;
-       memcpy(cpdest, xdr->p, cplen);
+       p = __xdr_inline_decode(xdr, cplen);
+       if (p == NULL)
+               return NULL;
+       memcpy(cpdest, p, cplen);
        cpdest += cplen;
        nbytes -= cplen;
        if (!xdr_set_next_buffer(xdr))
index ea244b29138b0b86cf7860ce5c1e4605ade86a2a..685e6d225414ee55f57237873a902b4c313e54c4 100644 (file)
@@ -1295,7 +1295,7 @@ void xprt_release(struct rpc_task *task)
        xprt_schedule_autodisconnect(xprt);
        spin_unlock_bh(&xprt->transport_lock);
        if (req->rq_buffer)
-               xprt->ops->buf_free(req->rq_buffer);
+               xprt->ops->buf_free(task);
        xprt_inject_disconnect(xprt);
        if (req->rq_cred != NULL)
                put_rpccred(req->rq_cred);
index 66c9d63f4797bbd9a9ffd655925797dce9503952..ae92a9e9ba52fe5424289156cca444887560f4c0 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/cmpxchg.h>
 #include <linux/spinlock.h>
 #include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/addr.h>
 #include <linux/sunrpc/xprtmultipath.h>
 
 typedef struct rpc_xprt *(*xprt_switch_find_xprt_t)(struct list_head *head,
@@ -49,7 +50,8 @@ void rpc_xprt_switch_add_xprt(struct rpc_xprt_switch *xps,
        if (xprt == NULL)
                return;
        spin_lock(&xps->xps_lock);
-       if (xps->xps_net == xprt->xprt_net || xps->xps_net == NULL)
+       if ((xps->xps_net == xprt->xprt_net || xps->xps_net == NULL) &&
+           !rpc_xprt_switch_has_addr(xps, (struct sockaddr *)&xprt->addr))
                xprt_switch_add_xprt_locked(xps, xprt);
        spin_unlock(&xps->xps_lock);
 }
@@ -232,6 +234,26 @@ struct rpc_xprt *xprt_iter_current_entry(struct rpc_xprt_iter *xpi)
        return xprt_switch_find_current_entry(head, xpi->xpi_cursor);
 }
 
+bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,
+                             const struct sockaddr *sap)
+{
+       struct list_head *head;
+       struct rpc_xprt *pos;
+
+       if (xps == NULL || sap == NULL)
+               return false;
+
+       head = &xps->xps_xprt_list;
+       list_for_each_entry_rcu(pos, head, xprt_switch) {
+               if (rpc_cmp_addr_port(sap, (struct sockaddr *)&pos->addr)) {
+                       pr_info("RPC:   addr %s already in xprt switch\n",
+                               pos->address_strings[RPC_DISPLAY_ADDR]);
+                       return true;
+               }
+       }
+       return false;
+}
+
 static
 struct rpc_xprt *xprt_switch_find_next_entry(struct list_head *head,
                const struct rpc_xprt *cur)
index 87762d976b63b9a1ebc5cb7a8b2603ed17b8b010..2c472e1b4827b73a7deb4621097d5a0133e2aacd 100644 (file)
@@ -27,7 +27,7 @@ static void rpcrdma_bc_free_rqst(struct rpcrdma_xprt *r_xprt,
        list_del(&req->rl_all);
        spin_unlock(&buf->rb_reqslock);
 
-       rpcrdma_destroy_req(&r_xprt->rx_ia, req);
+       rpcrdma_destroy_req(req);
 
        kfree(rqst);
 }
@@ -35,10 +35,8 @@ static void rpcrdma_bc_free_rqst(struct rpcrdma_xprt *r_xprt,
 static int rpcrdma_bc_setup_rqst(struct rpcrdma_xprt *r_xprt,
                                 struct rpc_rqst *rqst)
 {
-       struct rpcrdma_ia *ia = &r_xprt->rx_ia;
        struct rpcrdma_regbuf *rb;
        struct rpcrdma_req *req;
-       struct xdr_buf *buf;
        size_t size;
 
        req = rpcrdma_create_req(r_xprt);
@@ -46,30 +44,19 @@ static int rpcrdma_bc_setup_rqst(struct rpcrdma_xprt *r_xprt,
                return PTR_ERR(req);
        req->rl_backchannel = true;
 
-       size = RPCRDMA_INLINE_WRITE_THRESHOLD(rqst);
-       rb = rpcrdma_alloc_regbuf(ia, size, GFP_KERNEL);
+       rb = rpcrdma_alloc_regbuf(RPCRDMA_HDRBUF_SIZE,
+                                 DMA_TO_DEVICE, GFP_KERNEL);
        if (IS_ERR(rb))
                goto out_fail;
        req->rl_rdmabuf = rb;
 
-       size += RPCRDMA_INLINE_READ_THRESHOLD(rqst);
-       rb = rpcrdma_alloc_regbuf(ia, size, GFP_KERNEL);
+       size = r_xprt->rx_data.inline_rsize;
+       rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, GFP_KERNEL);
        if (IS_ERR(rb))
                goto out_fail;
-       rb->rg_owner = req;
        req->rl_sendbuf = rb;
-       /* so that rpcr_to_rdmar works when receiving a request */
-       rqst->rq_buffer = (void *)req->rl_sendbuf->rg_base;
-
-       buf = &rqst->rq_snd_buf;
-       buf->head[0].iov_base = rqst->rq_buffer;
-       buf->head[0].iov_len = 0;
-       buf->tail[0].iov_base = NULL;
-       buf->tail[0].iov_len = 0;
-       buf->page_len = 0;
-       buf->len = 0;
-       buf->buflen = size;
-
+       xdr_buf_init(&rqst->rq_snd_buf, rb->rg_base, size);
+       rpcrdma_set_xprtdata(rqst, req);
        return 0;
 
 out_fail:
@@ -219,7 +206,6 @@ int rpcrdma_bc_marshal_reply(struct rpc_rqst *rqst)
        struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt);
        struct rpcrdma_req *req = rpcr_to_rdmar(rqst);
        struct rpcrdma_msg *headerp;
-       size_t rpclen;
 
        headerp = rdmab_to_msg(req->rl_rdmabuf);
        headerp->rm_xid = rqst->rq_xid;
@@ -231,26 +217,9 @@ int rpcrdma_bc_marshal_reply(struct rpc_rqst *rqst)
        headerp->rm_body.rm_chunks[1] = xdr_zero;
        headerp->rm_body.rm_chunks[2] = xdr_zero;
 
-       rpclen = rqst->rq_svec[0].iov_len;
-
-#ifdef RPCRDMA_BACKCHANNEL_DEBUG
-       pr_info("RPC:       %s: rpclen %zd headerp 0x%p lkey 0x%x\n",
-               __func__, rpclen, headerp, rdmab_lkey(req->rl_rdmabuf));
-       pr_info("RPC:       %s: RPC/RDMA: %*ph\n",
-               __func__, (int)RPCRDMA_HDRLEN_MIN, headerp);
-       pr_info("RPC:       %s:      RPC: %*ph\n",
-               __func__, (int)rpclen, rqst->rq_svec[0].iov_base);
-#endif
-
-       req->rl_send_iov[0].addr = rdmab_addr(req->rl_rdmabuf);
-       req->rl_send_iov[0].length = RPCRDMA_HDRLEN_MIN;
-       req->rl_send_iov[0].lkey = rdmab_lkey(req->rl_rdmabuf);
-
-       req->rl_send_iov[1].addr = rdmab_addr(req->rl_sendbuf);
-       req->rl_send_iov[1].length = rpclen;
-       req->rl_send_iov[1].lkey = rdmab_lkey(req->rl_sendbuf);
-
-       req->rl_niovs = 2;
+       if (!rpcrdma_prepare_send_sges(&r_xprt->rx_ia, req, RPCRDMA_HDRLEN_MIN,
+                                      &rqst->rq_snd_buf, rpcrdma_noch))
+               return -EIO;
        return 0;
 }
 
@@ -402,7 +371,7 @@ out_overflow:
 out_short:
        pr_warn("RPC/RDMA short backward direction call\n");
 
-       if (rpcrdma_ep_post_recv(&r_xprt->rx_ia, &r_xprt->rx_ep, rep))
+       if (rpcrdma_ep_post_recv(&r_xprt->rx_ia, rep))
                xprt_disconnect_done(xprt);
        else
                pr_warn("RPC:       %s: reposting rep %p\n",
index 21cb3b150b371dc5e8aab5e02bc4af99a6a3d856..1ebb09e1ac4f8766cd99037b5d02b3b2b3965fae 100644 (file)
@@ -160,9 +160,8 @@ static int
 fmr_op_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep,
            struct rpcrdma_create_data_internal *cdata)
 {
-       rpcrdma_set_max_header_sizes(ia, cdata, max_t(unsigned int, 1,
-                                                     RPCRDMA_MAX_DATA_SEGS /
-                                                     RPCRDMA_MAX_FMR_SGES));
+       ia->ri_max_segs = max_t(unsigned int, 1, RPCRDMA_MAX_DATA_SEGS /
+                               RPCRDMA_MAX_FMR_SGES);
        return 0;
 }
 
@@ -274,6 +273,7 @@ fmr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
         */
        list_for_each_entry(mw, &req->rl_registered, mw_list)
                list_add_tail(&mw->fmr.fm_mr->list, &unmap_list);
+       r_xprt->rx_stats.local_inv_needed++;
        rc = ib_unmap_fmr(&unmap_list);
        if (rc)
                goto out_reset;
@@ -331,4 +331,5 @@ const struct rpcrdma_memreg_ops rpcrdma_fmr_memreg_ops = {
        .ro_init_mr                     = fmr_op_init_mr,
        .ro_release_mr                  = fmr_op_release_mr,
        .ro_displayname                 = "fmr",
+       .ro_send_w_inv_ok               = 0,
 };
index 892b5e1d9b099b217fba65a26b47105a0c027c51..210949562786665ea1a2990e2e9e6f886e580af6 100644 (file)
@@ -67,6 +67,8 @@
  * pending send queue WRs before the transport is reconnected.
  */
 
+#include <linux/sunrpc/rpc_rdma.h>
+
 #include "xprt_rdma.h"
 
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
@@ -161,7 +163,7 @@ __frwr_reset_mr(struct rpcrdma_ia *ia, struct rpcrdma_mw *r)
                return PTR_ERR(f->fr_mr);
        }
 
-       dprintk("RPC:       %s: recovered FRMR %p\n", __func__, r);
+       dprintk("RPC:       %s: recovered FRMR %p\n", __func__, f);
        f->fr_state = FRMR_IS_INVALID;
        return 0;
 }
@@ -242,9 +244,8 @@ frwr_op_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep,
                                               depth;
        }
 
-       rpcrdma_set_max_header_sizes(ia, cdata, max_t(unsigned int, 1,
-                                                     RPCRDMA_MAX_DATA_SEGS /
-                                                     ia->ri_max_frmr_depth));
+       ia->ri_max_segs = max_t(unsigned int, 1, RPCRDMA_MAX_DATA_SEGS /
+                               ia->ri_max_frmr_depth);
        return 0;
 }
 
@@ -329,7 +330,7 @@ frwr_wc_localinv_wake(struct ib_cq *cq, struct ib_wc *wc)
        frmr = container_of(cqe, struct rpcrdma_frmr, fr_cqe);
        if (wc->status != IB_WC_SUCCESS)
                __frwr_sendcompletion_flush(wc, frmr, "localinv");
-       complete_all(&frmr->fr_linv_done);
+       complete(&frmr->fr_linv_done);
 }
 
 /* Post a REG_MR Work Request to register a memory region
@@ -396,7 +397,7 @@ frwr_op_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
                goto out_mapmr_err;
 
        dprintk("RPC:       %s: Using frmr %p to map %u segments (%u bytes)\n",
-               __func__, mw, mw->mw_nents, mr->length);
+               __func__, frmr, mw->mw_nents, mr->length);
 
        key = (u8)(mr->rkey & 0x000000FF);
        ib_update_fast_reg_key(mr, ++key);
@@ -449,6 +450,8 @@ __frwr_prepare_linv_wr(struct rpcrdma_mw *mw)
        struct rpcrdma_frmr *f = &mw->frmr;
        struct ib_send_wr *invalidate_wr;
 
+       dprintk("RPC:       %s: invalidating frmr %p\n", __func__, f);
+
        f->fr_state = FRMR_IS_INVALID;
        invalidate_wr = &f->fr_invwr;
 
@@ -472,6 +475,7 @@ static void
 frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
 {
        struct ib_send_wr *invalidate_wrs, *pos, *prev, *bad_wr;
+       struct rpcrdma_rep *rep = req->rl_reply;
        struct rpcrdma_ia *ia = &r_xprt->rx_ia;
        struct rpcrdma_mw *mw, *tmp;
        struct rpcrdma_frmr *f;
@@ -487,6 +491,12 @@ frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
        f = NULL;
        invalidate_wrs = pos = prev = NULL;
        list_for_each_entry(mw, &req->rl_registered, mw_list) {
+               if ((rep->rr_wc_flags & IB_WC_WITH_INVALIDATE) &&
+                   (mw->mw_handle == rep->rr_inv_rkey)) {
+                       mw->frmr.fr_state = FRMR_IS_INVALID;
+                       continue;
+               }
+
                pos = __frwr_prepare_linv_wr(mw);
 
                if (!invalidate_wrs)
@@ -496,6 +506,8 @@ frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
                prev = pos;
                f = &mw->frmr;
        }
+       if (!f)
+               goto unmap;
 
        /* Strong send queue ordering guarantees that when the
         * last WR in the chain completes, all WRs in the chain
@@ -510,6 +522,7 @@ frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
         * replaces the QP. The RPC reply handler won't call us
         * unless ri_id->qp is a valid pointer.
         */
+       r_xprt->rx_stats.local_inv_needed++;
        rc = ib_post_send(ia->ri_id->qp, invalidate_wrs, &bad_wr);
        if (rc)
                goto reset_mrs;
@@ -521,6 +534,8 @@ frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
         */
 unmap:
        list_for_each_entry_safe(mw, tmp, &req->rl_registered, mw_list) {
+               dprintk("RPC:       %s: unmapping frmr %p\n",
+                       __func__, &mw->frmr);
                list_del_init(&mw->mw_list);
                ib_dma_unmap_sg(ia->ri_device,
                                mw->mw_sg, mw->mw_nents, mw->mw_dir);
@@ -576,4 +591,5 @@ const struct rpcrdma_memreg_ops rpcrdma_frwr_memreg_ops = {
        .ro_init_mr                     = frwr_op_init_mr,
        .ro_release_mr                  = frwr_op_release_mr,
        .ro_displayname                 = "frwr",
+       .ro_send_w_inv_ok               = RPCRDMA_CMP_F_SND_W_INV_OK,
 };
index a47f170b20ef88d1ebe1f9ca406374dee1b84102..d987c2d3dd6e4c531b29c668091053a07b0f549f 100644 (file)
 # define RPCDBG_FACILITY       RPCDBG_TRANS
 #endif
 
-enum rpcrdma_chunktype {
-       rpcrdma_noch = 0,
-       rpcrdma_readch,
-       rpcrdma_areadch,
-       rpcrdma_writech,
-       rpcrdma_replych
-};
-
 static const char transfertypes[][12] = {
        "inline",       /* no chunks */
        "read list",    /* some argument via rdma read */
@@ -118,10 +110,12 @@ static unsigned int rpcrdma_max_reply_header_size(unsigned int maxsegs)
        return size;
 }
 
-void rpcrdma_set_max_header_sizes(struct rpcrdma_ia *ia,
-                                 struct rpcrdma_create_data_internal *cdata,
-                                 unsigned int maxsegs)
+void rpcrdma_set_max_header_sizes(struct rpcrdma_xprt *r_xprt)
 {
+       struct rpcrdma_create_data_internal *cdata = &r_xprt->rx_data;
+       struct rpcrdma_ia *ia = &r_xprt->rx_ia;
+       unsigned int maxsegs = ia->ri_max_segs;
+
        ia->ri_max_inline_write = cdata->inline_wsize -
                                  rpcrdma_max_call_header_size(maxsegs);
        ia->ri_max_inline_read = cdata->inline_rsize -
@@ -155,42 +149,6 @@ static bool rpcrdma_results_inline(struct rpcrdma_xprt *r_xprt,
        return rqst->rq_rcv_buf.buflen <= ia->ri_max_inline_read;
 }
 
-static int
-rpcrdma_tail_pullup(struct xdr_buf *buf)
-{
-       size_t tlen = buf->tail[0].iov_len;
-       size_t skip = tlen & 3;
-
-       /* Do not include the tail if it is only an XDR pad */
-       if (tlen < 4)
-               return 0;
-
-       /* xdr_write_pages() adds a pad at the beginning of the tail
-        * if the content in "buf->pages" is unaligned. Force the
-        * tail's actual content to land at the next XDR position
-        * after the head instead.
-        */
-       if (skip) {
-               unsigned char *src, *dst;
-               unsigned int count;
-
-               src = buf->tail[0].iov_base;
-               dst = buf->head[0].iov_base;
-               dst += buf->head[0].iov_len;
-
-               src += skip;
-               tlen -= skip;
-
-               dprintk("RPC:       %s: skip=%zu, memmove(%p, %p, %zu)\n",
-                       __func__, skip, dst, src, tlen);
-
-               for (count = tlen; count; count--)
-                       *dst++ = *src++;
-       }
-
-       return tlen;
-}
-
 /* Split "vec" on page boundaries into segments. FMR registers pages,
  * not a byte range. Other modes coalesce these segments into a single
  * MR when they can.
@@ -229,7 +187,8 @@ rpcrdma_convert_kvec(struct kvec *vec, struct rpcrdma_mr_seg *seg, int n)
 
 static int
 rpcrdma_convert_iovs(struct xdr_buf *xdrbuf, unsigned int pos,
-       enum rpcrdma_chunktype type, struct rpcrdma_mr_seg *seg)
+       enum rpcrdma_chunktype type, struct rpcrdma_mr_seg *seg,
+       bool reminv_expected)
 {
        int len, n, p, page_base;
        struct page **ppages;
@@ -271,6 +230,13 @@ rpcrdma_convert_iovs(struct xdr_buf *xdrbuf, unsigned int pos,
        if (type == rpcrdma_readch)
                return n;
 
+       /* When encoding the Write list, some servers need to see an extra
+        * segment for odd-length Write chunks. The upper layer provides
+        * space in the tail iovec for this purpose.
+        */
+       if (type == rpcrdma_writech && reminv_expected)
+               return n;
+
        if (xdrbuf->tail[0].iov_len) {
                /* the rpcrdma protocol allows us to omit any trailing
                 * xdr pad bytes, saving the server an RDMA operation. */
@@ -327,7 +293,7 @@ rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt,
        if (rtype == rpcrdma_areadch)
                pos = 0;
        seg = req->rl_segments;
-       nsegs = rpcrdma_convert_iovs(&rqst->rq_snd_buf, pos, rtype, seg);
+       nsegs = rpcrdma_convert_iovs(&rqst->rq_snd_buf, pos, rtype, seg, false);
        if (nsegs < 0)
                return ERR_PTR(nsegs);
 
@@ -391,7 +357,8 @@ rpcrdma_encode_write_list(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
        seg = req->rl_segments;
        nsegs = rpcrdma_convert_iovs(&rqst->rq_rcv_buf,
                                     rqst->rq_rcv_buf.head[0].iov_len,
-                                    wtype, seg);
+                                    wtype, seg,
+                                    r_xprt->rx_ia.ri_reminv_expected);
        if (nsegs < 0)
                return ERR_PTR(nsegs);
 
@@ -456,7 +423,8 @@ rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt,
        }
 
        seg = req->rl_segments;
-       nsegs = rpcrdma_convert_iovs(&rqst->rq_rcv_buf, 0, wtype, seg);
+       nsegs = rpcrdma_convert_iovs(&rqst->rq_rcv_buf, 0, wtype, seg,
+                                    r_xprt->rx_ia.ri_reminv_expected);
        if (nsegs < 0)
                return ERR_PTR(nsegs);
 
@@ -491,74 +459,184 @@ rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt,
        return iptr;
 }
 
-/*
- * Copy write data inline.
- * This function is used for "small" requests. Data which is passed
- * to RPC via iovecs (or page list) is copied directly into the
- * pre-registered memory buffer for this request. For small amounts
- * of data, this is efficient. The cutoff value is tunable.
+/* Prepare the RPC-over-RDMA header SGE.
  */
-static void rpcrdma_inline_pullup(struct rpc_rqst *rqst)
+static bool
+rpcrdma_prepare_hdr_sge(struct rpcrdma_ia *ia, struct rpcrdma_req *req,
+                       u32 len)
 {
-       int i, npages, curlen;
-       int copy_len;
-       unsigned char *srcp, *destp;
-       struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt);
-       int page_base;
-       struct page **ppages;
+       struct rpcrdma_regbuf *rb = req->rl_rdmabuf;
+       struct ib_sge *sge = &req->rl_send_sge[0];
+
+       if (unlikely(!rpcrdma_regbuf_is_mapped(rb))) {
+               if (!__rpcrdma_dma_map_regbuf(ia, rb))
+                       return false;
+               sge->addr = rdmab_addr(rb);
+               sge->lkey = rdmab_lkey(rb);
+       }
+       sge->length = len;
+
+       ib_dma_sync_single_for_device(ia->ri_device, sge->addr,
+                                     sge->length, DMA_TO_DEVICE);
+       req->rl_send_wr.num_sge++;
+       return true;
+}
 
-       destp = rqst->rq_svec[0].iov_base;
-       curlen = rqst->rq_svec[0].iov_len;
-       destp += curlen;
+/* Prepare the Send SGEs. The head and tail iovec, and each entry
+ * in the page list, gets its own SGE.
+ */
+static bool
+rpcrdma_prepare_msg_sges(struct rpcrdma_ia *ia, struct rpcrdma_req *req,
+                        struct xdr_buf *xdr, enum rpcrdma_chunktype rtype)
+{
+       unsigned int sge_no, page_base, len, remaining;
+       struct rpcrdma_regbuf *rb = req->rl_sendbuf;
+       struct ib_device *device = ia->ri_device;
+       struct ib_sge *sge = req->rl_send_sge;
+       u32 lkey = ia->ri_pd->local_dma_lkey;
+       struct page *page, **ppages;
+
+       /* The head iovec is straightforward, as it is already
+        * DMA-mapped. Sync the content that has changed.
+        */
+       if (!rpcrdma_dma_map_regbuf(ia, rb))
+               return false;
+       sge_no = 1;
+       sge[sge_no].addr = rdmab_addr(rb);
+       sge[sge_no].length = xdr->head[0].iov_len;
+       sge[sge_no].lkey = rdmab_lkey(rb);
+       ib_dma_sync_single_for_device(device, sge[sge_no].addr,
+                                     sge[sge_no].length, DMA_TO_DEVICE);
+
+       /* If there is a Read chunk, the page list is being handled
+        * via explicit RDMA, and thus is skipped here. However, the
+        * tail iovec may include an XDR pad for the page list, as
+        * well as additional content, and may not reside in the
+        * same page as the head iovec.
+        */
+       if (rtype == rpcrdma_readch) {
+               len = xdr->tail[0].iov_len;
 
-       dprintk("RPC:       %s: destp 0x%p len %d hdrlen %d\n",
-               __func__, destp, rqst->rq_slen, curlen);
+               /* Do not include the tail if it is only an XDR pad */
+               if (len < 4)
+                       goto out;
 
-       copy_len = rqst->rq_snd_buf.page_len;
+               page = virt_to_page(xdr->tail[0].iov_base);
+               page_base = (unsigned long)xdr->tail[0].iov_base & ~PAGE_MASK;
 
-       if (rqst->rq_snd_buf.tail[0].iov_len) {
-               curlen = rqst->rq_snd_buf.tail[0].iov_len;
-               if (destp + copy_len != rqst->rq_snd_buf.tail[0].iov_base) {
-                       memmove(destp + copy_len,
-                               rqst->rq_snd_buf.tail[0].iov_base, curlen);
-                       r_xprt->rx_stats.pullup_copy_count += curlen;
+               /* If the content in the page list is an odd length,
+                * xdr_write_pages() has added a pad at the beginning
+                * of the tail iovec. Force the tail's non-pad content
+                * to land at the next XDR position in the Send message.
+                */
+               page_base += len & 3;
+               len -= len & 3;
+               goto map_tail;
+       }
+
+       /* If there is a page list present, temporarily DMA map
+        * and prepare an SGE for each page to be sent.
+        */
+       if (xdr->page_len) {
+               ppages = xdr->pages + (xdr->page_base >> PAGE_SHIFT);
+               page_base = xdr->page_base & ~PAGE_MASK;
+               remaining = xdr->page_len;
+               while (remaining) {
+                       sge_no++;
+                       if (sge_no > RPCRDMA_MAX_SEND_SGES - 2)
+                               goto out_mapping_overflow;
+
+                       len = min_t(u32, PAGE_SIZE - page_base, remaining);
+                       sge[sge_no].addr = ib_dma_map_page(device, *ppages,
+                                                          page_base, len,
+                                                          DMA_TO_DEVICE);
+                       if (ib_dma_mapping_error(device, sge[sge_no].addr))
+                               goto out_mapping_err;
+                       sge[sge_no].length = len;
+                       sge[sge_no].lkey = lkey;
+
+                       req->rl_mapped_sges++;
+                       ppages++;
+                       remaining -= len;
+                       page_base = 0;
                }
-               dprintk("RPC:       %s: tail destp 0x%p len %d\n",
-                       __func__, destp + copy_len, curlen);
-               rqst->rq_svec[0].iov_len += curlen;
        }
-       r_xprt->rx_stats.pullup_copy_count += copy_len;
 
-       page_base = rqst->rq_snd_buf.page_base;
-       ppages = rqst->rq_snd_buf.pages + (page_base >> PAGE_SHIFT);
-       page_base &= ~PAGE_MASK;
-       npages = PAGE_ALIGN(page_base+copy_len) >> PAGE_SHIFT;
-       for (i = 0; copy_len && i < npages; i++) {
-               curlen = PAGE_SIZE - page_base;
-               if (curlen > copy_len)
-                       curlen = copy_len;
-               dprintk("RPC:       %s: page %d destp 0x%p len %d curlen %d\n",
-                       __func__, i, destp, copy_len, curlen);
-               srcp = kmap_atomic(ppages[i]);
-               memcpy(destp, srcp+page_base, curlen);
-               kunmap_atomic(srcp);
-               rqst->rq_svec[0].iov_len += curlen;
-               destp += curlen;
-               copy_len -= curlen;
-               page_base = 0;
+       /* The tail iovec is not always constructed in the same
+        * page where the head iovec resides (see, for example,
+        * gss_wrap_req_priv). To neatly accommodate that case,
+        * DMA map it separately.
+        */
+       if (xdr->tail[0].iov_len) {
+               page = virt_to_page(xdr->tail[0].iov_base);
+               page_base = (unsigned long)xdr->tail[0].iov_base & ~PAGE_MASK;
+               len = xdr->tail[0].iov_len;
+
+map_tail:
+               sge_no++;
+               sge[sge_no].addr = ib_dma_map_page(device, page,
+                                                  page_base, len,
+                                                  DMA_TO_DEVICE);
+               if (ib_dma_mapping_error(device, sge[sge_no].addr))
+                       goto out_mapping_err;
+               sge[sge_no].length = len;
+               sge[sge_no].lkey = lkey;
+               req->rl_mapped_sges++;
        }
-       /* header now contains entire send message */
+
+out:
+       req->rl_send_wr.num_sge = sge_no + 1;
+       return true;
+
+out_mapping_overflow:
+       pr_err("rpcrdma: too many Send SGEs (%u)\n", sge_no);
+       return false;
+
+out_mapping_err:
+       pr_err("rpcrdma: Send mapping error\n");
+       return false;
+}
+
+bool
+rpcrdma_prepare_send_sges(struct rpcrdma_ia *ia, struct rpcrdma_req *req,
+                         u32 hdrlen, struct xdr_buf *xdr,
+                         enum rpcrdma_chunktype rtype)
+{
+       req->rl_send_wr.num_sge = 0;
+       req->rl_mapped_sges = 0;
+
+       if (!rpcrdma_prepare_hdr_sge(ia, req, hdrlen))
+               goto out_map;
+
+       if (rtype != rpcrdma_areadch)
+               if (!rpcrdma_prepare_msg_sges(ia, req, xdr, rtype))
+                       goto out_map;
+
+       return true;
+
+out_map:
+       pr_err("rpcrdma: failed to DMA map a Send buffer\n");
+       return false;
+}
+
+void
+rpcrdma_unmap_sges(struct rpcrdma_ia *ia, struct rpcrdma_req *req)
+{
+       struct ib_device *device = ia->ri_device;
+       struct ib_sge *sge;
+       int count;
+
+       sge = &req->rl_send_sge[2];
+       for (count = req->rl_mapped_sges; count--; sge++)
+               ib_dma_unmap_page(device, sge->addr, sge->length,
+                                 DMA_TO_DEVICE);
+       req->rl_mapped_sges = 0;
 }
 
 /*
  * Marshal a request: the primary job of this routine is to choose
  * the transfer modes. See comments below.
  *
- * Prepares up to two IOVs per Call message:
- *
- *  [0] -- RPC RDMA header
- *  [1] -- the RPC header/data
- *
  * Returns zero on success, otherwise a negative errno.
  */
 
@@ -626,12 +704,11 @@ rpcrdma_marshal_req(struct rpc_rqst *rqst)
         */
        if (rpcrdma_args_inline(r_xprt, rqst)) {
                rtype = rpcrdma_noch;
-               rpcrdma_inline_pullup(rqst);
-               rpclen = rqst->rq_svec[0].iov_len;
+               rpclen = rqst->rq_snd_buf.len;
        } else if (ddp_allowed && rqst->rq_snd_buf.flags & XDRBUF_WRITE) {
                rtype = rpcrdma_readch;
-               rpclen = rqst->rq_svec[0].iov_len;
-               rpclen += rpcrdma_tail_pullup(&rqst->rq_snd_buf);
+               rpclen = rqst->rq_snd_buf.head[0].iov_len +
+                        rqst->rq_snd_buf.tail[0].iov_len;
        } else {
                r_xprt->rx_stats.nomsg_call_count++;
                headerp->rm_type = htonl(RDMA_NOMSG);
@@ -673,34 +750,18 @@ rpcrdma_marshal_req(struct rpc_rqst *rqst)
                goto out_unmap;
        hdrlen = (unsigned char *)iptr - (unsigned char *)headerp;
 
-       if (hdrlen + rpclen > RPCRDMA_INLINE_WRITE_THRESHOLD(rqst))
-               goto out_overflow;
-
        dprintk("RPC: %5u %s: %s/%s: hdrlen %zd rpclen %zd\n",
                rqst->rq_task->tk_pid, __func__,
                transfertypes[rtype], transfertypes[wtype],
                hdrlen, rpclen);
 
-       req->rl_send_iov[0].addr = rdmab_addr(req->rl_rdmabuf);
-       req->rl_send_iov[0].length = hdrlen;
-       req->rl_send_iov[0].lkey = rdmab_lkey(req->rl_rdmabuf);
-
-       req->rl_niovs = 1;
-       if (rtype == rpcrdma_areadch)
-               return 0;
-
-       req->rl_send_iov[1].addr = rdmab_addr(req->rl_sendbuf);
-       req->rl_send_iov[1].length = rpclen;
-       req->rl_send_iov[1].lkey = rdmab_lkey(req->rl_sendbuf);
-
-       req->rl_niovs = 2;
+       if (!rpcrdma_prepare_send_sges(&r_xprt->rx_ia, req, hdrlen,
+                                      &rqst->rq_snd_buf, rtype)) {
+               iptr = ERR_PTR(-EIO);
+               goto out_unmap;
+       }
        return 0;
 
-out_overflow:
-       pr_err("rpcrdma: send overflow: hdrlen %zd rpclen %zu %s/%s\n",
-               hdrlen, rpclen, transfertypes[rtype], transfertypes[wtype]);
-       iptr = ERR_PTR(-EIO);
-
 out_unmap:
        r_xprt->rx_ia.ri_ops->ro_unmap_safe(r_xprt, req, false);
        return PTR_ERR(iptr);
@@ -916,8 +977,10 @@ rpcrdma_conn_func(struct rpcrdma_ep *ep)
  * allowed to timeout, to discover the errors at that time.
  */
 void
-rpcrdma_reply_handler(struct rpcrdma_rep *rep)
+rpcrdma_reply_handler(struct work_struct *work)
 {
+       struct rpcrdma_rep *rep =
+                       container_of(work, struct rpcrdma_rep, rr_work);
        struct rpcrdma_msg *headerp;
        struct rpcrdma_req *req;
        struct rpc_rqst *rqst;
@@ -1132,6 +1195,6 @@ out_duplicate:
 
 repost:
        r_xprt->rx_stats.bad_reply_count++;
-       if (rpcrdma_ep_post_recv(&r_xprt->rx_ia, &r_xprt->rx_ep, rep))
+       if (rpcrdma_ep_post_recv(&r_xprt->rx_ia, rep))
                rpcrdma_recv_buffer_put(rep);
 }
index a2a7519b0f23575d3b7e891973c5026634fe0537..2d8545c3409596190d027e8ad73651903f25a6aa 100644 (file)
@@ -129,7 +129,7 @@ static int svc_rdma_bc_sendto(struct svcxprt_rdma *rdma,
                ret = -EIO;
                goto out_unmap;
        }
-       atomic_inc(&rdma->sc_dma_used);
+       svc_rdma_count_mappings(rdma, ctxt);
 
        memset(&send_wr, 0, sizeof(send_wr));
        ctxt->cqe.done = svc_rdma_wc_send;
@@ -159,33 +159,34 @@ out_unmap:
 /* Server-side transport endpoint wants a whole page for its send
  * buffer. The client RPC code constructs the RPC header in this
  * buffer before it invokes ->send_request.
- *
- * Returns NULL if there was a temporary allocation failure.
  */
-static void *
-xprt_rdma_bc_allocate(struct rpc_task *task, size_t size)
+static int
+xprt_rdma_bc_allocate(struct rpc_task *task)
 {
        struct rpc_rqst *rqst = task->tk_rqstp;
        struct svc_xprt *sxprt = rqst->rq_xprt->bc_xprt;
+       size_t size = rqst->rq_callsize;
        struct svcxprt_rdma *rdma;
        struct page *page;
 
        rdma = container_of(sxprt, struct svcxprt_rdma, sc_xprt);
 
-       /* Prevent an infinite loop: try to make this case work */
-       if (size > PAGE_SIZE)
+       if (size > PAGE_SIZE) {
                WARN_ONCE(1, "svcrdma: large bc buffer request (size %zu)\n",
                          size);
+               return -EINVAL;
+       }
 
        page = alloc_page(RPCRDMA_DEF_GFP);
        if (!page)
-               return NULL;
+               return -ENOMEM;
 
-       return page_address(page);
+       rqst->rq_buffer = page_address(page);
+       return 0;
 }
 
 static void
-xprt_rdma_bc_free(void *buffer)
+xprt_rdma_bc_free(struct rpc_task *task)
 {
        /* No-op: ctxt and page have already been freed. */
 }
index 2c25606f25614da9fb181ead55c6c6d7a1546b62..ad1df979b3f07224457d5723d53946659adcae18 100644 (file)
@@ -159,7 +159,7 @@ int rdma_read_chunk_lcl(struct svcxprt_rdma *xprt,
                                           ctxt->sge[pno].addr);
                if (ret)
                        goto err;
-               atomic_inc(&xprt->sc_dma_used);
+               svc_rdma_count_mappings(xprt, ctxt);
 
                ctxt->sge[pno].lkey = xprt->sc_pd->local_dma_lkey;
                ctxt->sge[pno].length = len;
index 54d53330062030b682fcd8f267e7d79448c6a5ef..f5a91edcd2337c5e6d096634feb2c25f33747c3a 100644 (file)
@@ -225,6 +225,48 @@ svc_rdma_get_reply_array(struct rpcrdma_msg *rmsgp,
        return rp_ary;
 }
 
+/* RPC-over-RDMA Version One private extension: Remote Invalidation.
+ * Responder's choice: requester signals it can handle Send With
+ * Invalidate, and responder chooses one rkey to invalidate.
+ *
+ * Find a candidate rkey to invalidate when sending a reply.  Picks the
+ * first rkey it finds in the chunks lists.
+ *
+ * Returns zero if RPC's chunk lists are empty.
+ */
+static u32 svc_rdma_get_inv_rkey(struct rpcrdma_msg *rdma_argp,
+                                struct rpcrdma_write_array *wr_ary,
+                                struct rpcrdma_write_array *rp_ary)
+{
+       struct rpcrdma_read_chunk *rd_ary;
+       struct rpcrdma_segment *arg_ch;
+       u32 inv_rkey;
+
+       inv_rkey = 0;
+
+       rd_ary = svc_rdma_get_read_chunk(rdma_argp);
+       if (rd_ary) {
+               inv_rkey = be32_to_cpu(rd_ary->rc_target.rs_handle);
+               goto out;
+       }
+
+       if (wr_ary && be32_to_cpu(wr_ary->wc_nchunks)) {
+               arg_ch = &wr_ary->wc_array[0].wc_target;
+               inv_rkey = be32_to_cpu(arg_ch->rs_handle);
+               goto out;
+       }
+
+       if (rp_ary && be32_to_cpu(rp_ary->wc_nchunks)) {
+               arg_ch = &rp_ary->wc_array[0].wc_target;
+               inv_rkey = be32_to_cpu(arg_ch->rs_handle);
+               goto out;
+       }
+
+out:
+       dprintk("svcrdma: Send With Invalidate rkey=%08x\n", inv_rkey);
+       return inv_rkey;
+}
+
 /* Assumptions:
  * - The specified write_len can be represented in sc_max_sge * PAGE_SIZE
  */
@@ -280,7 +322,7 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
                if (ib_dma_mapping_error(xprt->sc_cm_id->device,
                                         sge[sge_no].addr))
                        goto err;
-               atomic_inc(&xprt->sc_dma_used);
+               svc_rdma_count_mappings(xprt, ctxt);
                sge[sge_no].lkey = xprt->sc_pd->local_dma_lkey;
                ctxt->count++;
                sge_off = 0;
@@ -464,7 +506,8 @@ static int send_reply(struct svcxprt_rdma *rdma,
                      struct page *page,
                      struct rpcrdma_msg *rdma_resp,
                      struct svc_rdma_req_map *vec,
-                     int byte_count)
+                     int byte_count,
+                     u32 inv_rkey)
 {
        struct svc_rdma_op_ctxt *ctxt;
        struct ib_send_wr send_wr;
@@ -489,7 +532,7 @@ static int send_reply(struct svcxprt_rdma *rdma,
                            ctxt->sge[0].length, DMA_TO_DEVICE);
        if (ib_dma_mapping_error(rdma->sc_cm_id->device, ctxt->sge[0].addr))
                goto err;
-       atomic_inc(&rdma->sc_dma_used);
+       svc_rdma_count_mappings(rdma, ctxt);
 
        ctxt->direction = DMA_TO_DEVICE;
 
@@ -505,7 +548,7 @@ static int send_reply(struct svcxprt_rdma *rdma,
                if (ib_dma_mapping_error(rdma->sc_cm_id->device,
                                         ctxt->sge[sge_no].addr))
                        goto err;
-               atomic_inc(&rdma->sc_dma_used);
+               svc_rdma_count_mappings(rdma, ctxt);
                ctxt->sge[sge_no].lkey = rdma->sc_pd->local_dma_lkey;
                ctxt->sge[sge_no].length = sge_bytes;
        }
@@ -523,23 +566,9 @@ static int send_reply(struct svcxprt_rdma *rdma,
                ctxt->pages[page_no+1] = rqstp->rq_respages[page_no];
                ctxt->count++;
                rqstp->rq_respages[page_no] = NULL;
-               /*
-                * If there are more pages than SGE, terminate SGE
-                * list so that svc_rdma_unmap_dma doesn't attempt to
-                * unmap garbage.
-                */
-               if (page_no+1 >= sge_no)
-                       ctxt->sge[page_no+1].length = 0;
        }
        rqstp->rq_next_page = rqstp->rq_respages + 1;
 
-       /* The loop above bumps sc_dma_used for each sge. The
-        * xdr_buf.tail gets a separate sge, but resides in the
-        * same page as xdr_buf.head. Don't count it twice.
-        */
-       if (sge_no > ctxt->count)
-               atomic_dec(&rdma->sc_dma_used);
-
        if (sge_no > rdma->sc_max_sge) {
                pr_err("svcrdma: Too many sges (%d)\n", sge_no);
                goto err;
@@ -549,7 +578,11 @@ static int send_reply(struct svcxprt_rdma *rdma,
        send_wr.wr_cqe = &ctxt->cqe;
        send_wr.sg_list = ctxt->sge;
        send_wr.num_sge = sge_no;
-       send_wr.opcode = IB_WR_SEND;
+       if (inv_rkey) {
+               send_wr.opcode = IB_WR_SEND_WITH_INV;
+               send_wr.ex.invalidate_rkey = inv_rkey;
+       } else
+               send_wr.opcode = IB_WR_SEND;
        send_wr.send_flags =  IB_SEND_SIGNALED;
 
        ret = svc_rdma_send(rdma, &send_wr);
@@ -581,6 +614,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
        int inline_bytes;
        struct page *res_page;
        struct svc_rdma_req_map *vec;
+       u32 inv_rkey;
 
        dprintk("svcrdma: sending response for rqstp=%p\n", rqstp);
 
@@ -591,6 +625,10 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
        wr_ary = svc_rdma_get_write_array(rdma_argp);
        rp_ary = svc_rdma_get_reply_array(rdma_argp, wr_ary);
 
+       inv_rkey = 0;
+       if (rdma->sc_snd_w_inv)
+               inv_rkey = svc_rdma_get_inv_rkey(rdma_argp, wr_ary, rp_ary);
+
        /* Build an req vec for the XDR */
        vec = svc_rdma_get_req_map(rdma);
        ret = svc_rdma_map_xdr(rdma, &rqstp->rq_res, vec, wr_ary != NULL);
@@ -633,9 +671,9 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
                goto err1;
 
        ret = send_reply(rdma, rqstp, res_page, rdma_resp, vec,
-                        inline_bytes);
+                        inline_bytes, inv_rkey);
        if (ret < 0)
-               goto err1;
+               goto err0;
 
        svc_rdma_put_req_map(rdma, vec);
        dprintk("svcrdma: send_reply returns %d\n", ret);
@@ -692,7 +730,7 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
                svc_rdma_put_context(ctxt, 1);
                return;
        }
-       atomic_inc(&xprt->sc_dma_used);
+       svc_rdma_count_mappings(xprt, ctxt);
 
        /* Prepare SEND WR */
        memset(&err_wr, 0, sizeof(err_wr));
index eb2857f52b05134baa1263a9942d4f0b704e9c2c..6864fb967038d3bc8c410502f11a56aa442c661e 100644 (file)
@@ -198,6 +198,7 @@ struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt)
 
 out:
        ctxt->count = 0;
+       ctxt->mapped_sges = 0;
        ctxt->frmr = NULL;
        return ctxt;
 
@@ -221,22 +222,27 @@ out_empty:
 void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt)
 {
        struct svcxprt_rdma *xprt = ctxt->xprt;
-       int i;
-       for (i = 0; i < ctxt->count && ctxt->sge[i].length; i++) {
+       struct ib_device *device = xprt->sc_cm_id->device;
+       u32 lkey = xprt->sc_pd->local_dma_lkey;
+       unsigned int i, count;
+
+       for (count = 0, i = 0; i < ctxt->mapped_sges; i++) {
                /*
                 * Unmap the DMA addr in the SGE if the lkey matches
                 * the local_dma_lkey, otherwise, ignore it since it is
                 * an FRMR lkey and will be unmapped later when the
                 * last WR that uses it completes.
                 */
-               if (ctxt->sge[i].lkey == xprt->sc_pd->local_dma_lkey) {
-                       atomic_dec(&xprt->sc_dma_used);
-                       ib_dma_unmap_page(xprt->sc_cm_id->device,
+               if (ctxt->sge[i].lkey == lkey) {
+                       count++;
+                       ib_dma_unmap_page(device,
                                            ctxt->sge[i].addr,
                                            ctxt->sge[i].length,
                                            ctxt->direction);
                }
        }
+       ctxt->mapped_sges = 0;
+       atomic_sub(count, &xprt->sc_dma_used);
 }
 
 void svc_rdma_put_context(struct svc_rdma_op_ctxt *ctxt, int free_pages)
@@ -600,7 +606,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt, gfp_t flags)
                                     DMA_FROM_DEVICE);
                if (ib_dma_mapping_error(xprt->sc_cm_id->device, pa))
                        goto err_put_ctxt;
-               atomic_inc(&xprt->sc_dma_used);
+               svc_rdma_count_mappings(xprt, ctxt);
                ctxt->sge[sge_no].addr = pa;
                ctxt->sge[sge_no].length = PAGE_SIZE;
                ctxt->sge[sge_no].lkey = xprt->sc_pd->local_dma_lkey;
@@ -642,6 +648,26 @@ int svc_rdma_repost_recv(struct svcxprt_rdma *xprt, gfp_t flags)
        return ret;
 }
 
+static void
+svc_rdma_parse_connect_private(struct svcxprt_rdma *newxprt,
+                              struct rdma_conn_param *param)
+{
+       const struct rpcrdma_connect_private *pmsg = param->private_data;
+
+       if (pmsg &&
+           pmsg->cp_magic == rpcrdma_cmp_magic &&
+           pmsg->cp_version == RPCRDMA_CMP_VERSION) {
+               newxprt->sc_snd_w_inv = pmsg->cp_flags &
+                                       RPCRDMA_CMP_F_SND_W_INV_OK;
+
+               dprintk("svcrdma: client send_size %u, recv_size %u "
+                       "remote inv %ssupported\n",
+                       rpcrdma_decode_buffer_size(pmsg->cp_send_size),
+                       rpcrdma_decode_buffer_size(pmsg->cp_recv_size),
+                       newxprt->sc_snd_w_inv ? "" : "un");
+       }
+}
+
 /*
  * This function handles the CONNECT_REQUEST event on a listening
  * endpoint. It is passed the cma_id for the _new_ connection. The context in
@@ -653,7 +679,8 @@ int svc_rdma_repost_recv(struct svcxprt_rdma *xprt, gfp_t flags)
  * will call the recvfrom method on the listen xprt which will accept the new
  * connection.
  */
-static void handle_connect_req(struct rdma_cm_id *new_cma_id, size_t client_ird)
+static void handle_connect_req(struct rdma_cm_id *new_cma_id,
+                              struct rdma_conn_param *param)
 {
        struct svcxprt_rdma *listen_xprt = new_cma_id->context;
        struct svcxprt_rdma *newxprt;
@@ -669,9 +696,10 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id, size_t client_ird)
        new_cma_id->context = newxprt;
        dprintk("svcrdma: Creating newxprt=%p, cm_id=%p, listenxprt=%p\n",
                newxprt, newxprt->sc_cm_id, listen_xprt);
+       svc_rdma_parse_connect_private(newxprt, param);
 
        /* Save client advertised inbound read limit for use later in accept. */
-       newxprt->sc_ord = client_ird;
+       newxprt->sc_ord = param->initiator_depth;
 
        /* Set the local and remote addresses in the transport */
        sa = (struct sockaddr *)&newxprt->sc_cm_id->route.addr.dst_addr;
@@ -706,8 +734,7 @@ static int rdma_listen_handler(struct rdma_cm_id *cma_id,
                dprintk("svcrdma: Connect request on cma_id=%p, xprt = %p, "
                        "event = %s (%d)\n", cma_id, cma_id->context,
                        rdma_event_msg(event->event), event->event);
-               handle_connect_req(cma_id,
-                                  event->param.conn.initiator_depth);
+               handle_connect_req(cma_id, &event->param.conn);
                break;
 
        case RDMA_CM_EVENT_ESTABLISHED:
@@ -941,6 +968,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
        struct svcxprt_rdma *listen_rdma;
        struct svcxprt_rdma *newxprt = NULL;
        struct rdma_conn_param conn_param;
+       struct rpcrdma_connect_private pmsg;
        struct ib_qp_init_attr qp_attr;
        struct ib_device *dev;
        unsigned int i;
@@ -1070,7 +1098,8 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
                        dev->attrs.max_fast_reg_page_list_len;
                newxprt->sc_dev_caps |= SVCRDMA_DEVCAP_FAST_REG;
                newxprt->sc_reader = rdma_read_chunk_frmr;
-       }
+       } else
+               newxprt->sc_snd_w_inv = false;
 
        /*
         * Determine if a DMA MR is required and if so, what privs are required
@@ -1094,11 +1123,20 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
        /* Swap out the handler */
        newxprt->sc_cm_id->event_handler = rdma_cma_handler;
 
+       /* Construct RDMA-CM private message */
+       pmsg.cp_magic = rpcrdma_cmp_magic;
+       pmsg.cp_version = RPCRDMA_CMP_VERSION;
+       pmsg.cp_flags = 0;
+       pmsg.cp_send_size = pmsg.cp_recv_size =
+               rpcrdma_encode_buffer_size(newxprt->sc_max_req_size);
+
        /* Accept Connection */
        set_bit(RDMAXPRT_CONN_PENDING, &newxprt->sc_flags);
        memset(&conn_param, 0, sizeof conn_param);
        conn_param.responder_resources = 0;
        conn_param.initiator_depth = newxprt->sc_ord;
+       conn_param.private_data = &pmsg;
+       conn_param.private_data_len = sizeof(pmsg);
        ret = rdma_accept(newxprt->sc_cm_id, &conn_param);
        if (ret) {
                dprintk("svcrdma: failed to accept new connection, ret=%d\n",
index 81f0e879f019e43d35cc9c85ead0dfd17ebc8d30..ed5e285fd2ea7f6f26bf08d2d1e8d28db7823f63 100644 (file)
@@ -97,7 +97,7 @@ static struct ctl_table xr_tunables_table[] = {
                .data           = &xprt_rdma_max_inline_read,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec,
+               .proc_handler   = proc_dointvec_minmax,
                .extra1         = &min_inline_size,
                .extra2         = &max_inline_size,
        },
@@ -106,7 +106,7 @@ static struct ctl_table xr_tunables_table[] = {
                .data           = &xprt_rdma_max_inline_write,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec,
+               .proc_handler   = proc_dointvec_minmax,
                .extra1         = &min_inline_size,
                .extra2         = &max_inline_size,
        },
@@ -477,115 +477,152 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task)
        }
 }
 
-/*
- * The RDMA allocate/free functions need the task structure as a place
- * to hide the struct rpcrdma_req, which is necessary for the actual send/recv
- * sequence.
+/* Allocate a fixed-size buffer in which to construct and send the
+ * RPC-over-RDMA header for this request.
+ */
+static bool
+rpcrdma_get_rdmabuf(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
+                   gfp_t flags)
+{
+       size_t size = RPCRDMA_HDRBUF_SIZE;
+       struct rpcrdma_regbuf *rb;
+
+       if (req->rl_rdmabuf)
+               return true;
+
+       rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, flags);
+       if (IS_ERR(rb))
+               return false;
+
+       r_xprt->rx_stats.hardway_register_count += size;
+       req->rl_rdmabuf = rb;
+       return true;
+}
+
+static bool
+rpcrdma_get_sendbuf(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
+                   size_t size, gfp_t flags)
+{
+       struct rpcrdma_regbuf *rb;
+
+       if (req->rl_sendbuf && rdmab_length(req->rl_sendbuf) >= size)
+               return true;
+
+       rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, flags);
+       if (IS_ERR(rb))
+               return false;
+
+       rpcrdma_free_regbuf(req->rl_sendbuf);
+       r_xprt->rx_stats.hardway_register_count += size;
+       req->rl_sendbuf = rb;
+       return true;
+}
+
+/* The rq_rcv_buf is used only if a Reply chunk is necessary.
+ * The decision to use a Reply chunk is made later in
+ * rpcrdma_marshal_req. This buffer is registered at that time.
  *
- * The RPC layer allocates both send and receive buffers in the same call
- * (rq_send_buf and rq_rcv_buf are both part of a single contiguous buffer).
- * We may register rq_rcv_buf when using reply chunks.
+ * Otherwise, the associated RPC Reply arrives in a separate
+ * Receive buffer, arbitrarily chosen by the HCA. The buffer
+ * allocated here for the RPC Reply is not utilized in that
+ * case. See rpcrdma_inline_fixup.
+ *
+ * A regbuf is used here to remember the buffer size.
  */
-static void *
-xprt_rdma_allocate(struct rpc_task *task, size_t size)
+static bool
+rpcrdma_get_recvbuf(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
+                   size_t size, gfp_t flags)
 {
-       struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
-       struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt);
        struct rpcrdma_regbuf *rb;
+
+       if (req->rl_recvbuf && rdmab_length(req->rl_recvbuf) >= size)
+               return true;
+
+       rb = rpcrdma_alloc_regbuf(size, DMA_NONE, flags);
+       if (IS_ERR(rb))
+               return false;
+
+       rpcrdma_free_regbuf(req->rl_recvbuf);
+       r_xprt->rx_stats.hardway_register_count += size;
+       req->rl_recvbuf = rb;
+       return true;
+}
+
+/**
+ * xprt_rdma_allocate - allocate transport resources for an RPC
+ * @task: RPC task
+ *
+ * Return values:
+ *        0:   Success; rq_buffer points to RPC buffer to use
+ *   ENOMEM:   Out of memory, call again later
+ *      EIO:   A permanent error occurred, do not retry
+ *
+ * The RDMA allocate/free functions need the task structure as a place
+ * to hide the struct rpcrdma_req, which is necessary for the actual
+ * send/recv sequence.
+ *
+ * xprt_rdma_allocate provides buffers that are already mapped for
+ * DMA, and a local DMA lkey is provided for each.
+ */
+static int
+xprt_rdma_allocate(struct rpc_task *task)
+{
+       struct rpc_rqst *rqst = task->tk_rqstp;
+       struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt);
        struct rpcrdma_req *req;
-       size_t min_size;
        gfp_t flags;
 
        req = rpcrdma_buffer_get(&r_xprt->rx_buf);
        if (req == NULL)
-               return NULL;
+               return -ENOMEM;
 
        flags = RPCRDMA_DEF_GFP;
        if (RPC_IS_SWAPPER(task))
                flags = __GFP_MEMALLOC | GFP_NOWAIT | __GFP_NOWARN;
 
-       if (req->rl_rdmabuf == NULL)
-               goto out_rdmabuf;
-       if (req->rl_sendbuf == NULL)
-               goto out_sendbuf;
-       if (size > req->rl_sendbuf->rg_size)
-               goto out_sendbuf;
-
-out:
-       dprintk("RPC:       %s: size %zd, request 0x%p\n", __func__, size, req);
-       req->rl_connect_cookie = 0;     /* our reserved value */
-       req->rl_task = task;
-       return req->rl_sendbuf->rg_base;
-
-out_rdmabuf:
-       min_size = RPCRDMA_INLINE_WRITE_THRESHOLD(task->tk_rqstp);
-       rb = rpcrdma_alloc_regbuf(&r_xprt->rx_ia, min_size, flags);
-       if (IS_ERR(rb))
+       if (!rpcrdma_get_rdmabuf(r_xprt, req, flags))
                goto out_fail;
-       req->rl_rdmabuf = rb;
-
-out_sendbuf:
-       /* XDR encoding and RPC/RDMA marshaling of this request has not
-        * yet occurred. Thus a lower bound is needed to prevent buffer
-        * overrun during marshaling.
-        *
-        * RPC/RDMA marshaling may choose to send payload bearing ops
-        * inline, if the result is smaller than the inline threshold.
-        * The value of the "size" argument accounts for header
-        * requirements but not for the payload in these cases.
-        *
-        * Likewise, allocate enough space to receive a reply up to the
-        * size of the inline threshold.
-        *
-        * It's unlikely that both the send header and the received
-        * reply will be large, but slush is provided here to allow
-        * flexibility when marshaling.
-        */
-       min_size = RPCRDMA_INLINE_READ_THRESHOLD(task->tk_rqstp);
-       min_size += RPCRDMA_INLINE_WRITE_THRESHOLD(task->tk_rqstp);
-       if (size < min_size)
-               size = min_size;
-
-       rb = rpcrdma_alloc_regbuf(&r_xprt->rx_ia, size, flags);
-       if (IS_ERR(rb))
+       if (!rpcrdma_get_sendbuf(r_xprt, req, rqst->rq_callsize, flags))
+               goto out_fail;
+       if (!rpcrdma_get_recvbuf(r_xprt, req, rqst->rq_rcvsize, flags))
                goto out_fail;
-       rb->rg_owner = req;
 
-       r_xprt->rx_stats.hardway_register_count += size;
-       rpcrdma_free_regbuf(&r_xprt->rx_ia, req->rl_sendbuf);
-       req->rl_sendbuf = rb;
-       goto out;
+       dprintk("RPC: %5u %s: send size = %zd, recv size = %zd, req = %p\n",
+               task->tk_pid, __func__, rqst->rq_callsize,
+               rqst->rq_rcvsize, req);
+
+       req->rl_connect_cookie = 0;     /* our reserved value */
+       rpcrdma_set_xprtdata(rqst, req);
+       rqst->rq_buffer = req->rl_sendbuf->rg_base;
+       rqst->rq_rbuffer = req->rl_recvbuf->rg_base;
+       return 0;
 
 out_fail:
        rpcrdma_buffer_put(req);
-       return NULL;
+       return -ENOMEM;
 }
 
-/*
- * This function returns all RDMA resources to the pool.
+/**
+ * xprt_rdma_free - release resources allocated by xprt_rdma_allocate
+ * @task: RPC task
+ *
+ * Caller guarantees rqst->rq_buffer is non-NULL.
  */
 static void
-xprt_rdma_free(void *buffer)
+xprt_rdma_free(struct rpc_task *task)
 {
-       struct rpcrdma_req *req;
-       struct rpcrdma_xprt *r_xprt;
-       struct rpcrdma_regbuf *rb;
-
-       if (buffer == NULL)
-               return;
+       struct rpc_rqst *rqst = task->tk_rqstp;
+       struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt);
+       struct rpcrdma_req *req = rpcr_to_rdmar(rqst);
+       struct rpcrdma_ia *ia = &r_xprt->rx_ia;
 
-       rb = container_of(buffer, struct rpcrdma_regbuf, rg_base[0]);
-       req = rb->rg_owner;
        if (req->rl_backchannel)
                return;
 
-       r_xprt = container_of(req->rl_buffer, struct rpcrdma_xprt, rx_buf);
-
        dprintk("RPC:       %s: called on 0x%p\n", __func__, req->rl_reply);
 
-       r_xprt->rx_ia.ri_ops->ro_unmap_safe(r_xprt, req,
-                                           !RPC_IS_ASYNC(req->rl_task));
-
+       ia->ri_ops->ro_unmap_safe(r_xprt, req, !RPC_IS_ASYNC(task));
+       rpcrdma_unmap_sges(ia, req);
        rpcrdma_buffer_put(req);
 }
 
@@ -685,10 +722,11 @@ void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
                   r_xprt->rx_stats.failed_marshal_count,
                   r_xprt->rx_stats.bad_reply_count,
                   r_xprt->rx_stats.nomsg_call_count);
-       seq_printf(seq, "%lu %lu %lu\n",
+       seq_printf(seq, "%lu %lu %lu %lu\n",
                   r_xprt->rx_stats.mrs_recovered,
                   r_xprt->rx_stats.mrs_orphaned,
-                  r_xprt->rx_stats.mrs_allocated);
+                  r_xprt->rx_stats.mrs_allocated,
+                  r_xprt->rx_stats.local_inv_needed);
 }
 
 static int
index be3178e5e2d24c4a46549060f464f3d3b4debd6a..ec74289af7ec90b7f33a2ec2e2157c588ed0eeb3 100644 (file)
@@ -129,15 +129,6 @@ rpcrdma_wc_send(struct ib_cq *cq, struct ib_wc *wc)
                       wc->status, wc->vendor_err);
 }
 
-static void
-rpcrdma_receive_worker(struct work_struct *work)
-{
-       struct rpcrdma_rep *rep =
-                       container_of(work, struct rpcrdma_rep, rr_work);
-
-       rpcrdma_reply_handler(rep);
-}
-
 /* Perform basic sanity checking to avoid using garbage
  * to update the credit grant value.
  */
@@ -161,13 +152,13 @@ rpcrdma_update_granted_credits(struct rpcrdma_rep *rep)
 }
 
 /**
- * rpcrdma_receive_wc - Invoked by RDMA provider for each polled Receive WC
+ * rpcrdma_wc_receive - Invoked by RDMA provider for each polled Receive WC
  * @cq:        completion queue (ignored)
  * @wc:        completed WR
  *
  */
 static void
-rpcrdma_receive_wc(struct ib_cq *cq, struct ib_wc *wc)
+rpcrdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc)
 {
        struct ib_cqe *cqe = wc->wr_cqe;
        struct rpcrdma_rep *rep = container_of(cqe, struct rpcrdma_rep,
@@ -185,6 +176,9 @@ rpcrdma_receive_wc(struct ib_cq *cq, struct ib_wc *wc)
                __func__, rep, wc->byte_len);
 
        rep->rr_len = wc->byte_len;
+       rep->rr_wc_flags = wc->wc_flags;
+       rep->rr_inv_rkey = wc->ex.invalidate_rkey;
+
        ib_dma_sync_single_for_cpu(rep->rr_device,
                                   rdmab_addr(rep->rr_rdmabuf),
                                   rep->rr_len, DMA_FROM_DEVICE);
@@ -204,6 +198,36 @@ out_fail:
        goto out_schedule;
 }
 
+static void
+rpcrdma_update_connect_private(struct rpcrdma_xprt *r_xprt,
+                              struct rdma_conn_param *param)
+{
+       struct rpcrdma_create_data_internal *cdata = &r_xprt->rx_data;
+       const struct rpcrdma_connect_private *pmsg = param->private_data;
+       unsigned int rsize, wsize;
+
+       /* Default settings for RPC-over-RDMA Version One */
+       r_xprt->rx_ia.ri_reminv_expected = false;
+       rsize = RPCRDMA_V1_DEF_INLINE_SIZE;
+       wsize = RPCRDMA_V1_DEF_INLINE_SIZE;
+
+       if (pmsg &&
+           pmsg->cp_magic == rpcrdma_cmp_magic &&
+           pmsg->cp_version == RPCRDMA_CMP_VERSION) {
+               r_xprt->rx_ia.ri_reminv_expected = true;
+               rsize = rpcrdma_decode_buffer_size(pmsg->cp_send_size);
+               wsize = rpcrdma_decode_buffer_size(pmsg->cp_recv_size);
+       }
+
+       if (rsize < cdata->inline_rsize)
+               cdata->inline_rsize = rsize;
+       if (wsize < cdata->inline_wsize)
+               cdata->inline_wsize = wsize;
+       pr_info("rpcrdma: max send %u, max recv %u\n",
+               cdata->inline_wsize, cdata->inline_rsize);
+       rpcrdma_set_max_header_sizes(r_xprt);
+}
+
 static int
 rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event)
 {
@@ -244,6 +268,7 @@ rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event)
                        " (%d initiator)\n",
                        __func__, attr->max_dest_rd_atomic,
                        attr->max_rd_atomic);
+               rpcrdma_update_connect_private(xprt, &event->param.conn);
                goto connected;
        case RDMA_CM_EVENT_CONNECT_ERROR:
                connstate = -ENOTCONN;
@@ -454,11 +479,12 @@ int
 rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
                                struct rpcrdma_create_data_internal *cdata)
 {
+       struct rpcrdma_connect_private *pmsg = &ep->rep_cm_private;
        struct ib_cq *sendcq, *recvcq;
        unsigned int max_qp_wr;
        int rc;
 
-       if (ia->ri_device->attrs.max_sge < RPCRDMA_MAX_IOVS) {
+       if (ia->ri_device->attrs.max_sge < RPCRDMA_MAX_SEND_SGES) {
                dprintk("RPC:       %s: insufficient sge's available\n",
                        __func__);
                return -ENOMEM;
@@ -487,7 +513,7 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
        ep->rep_attr.cap.max_recv_wr = cdata->max_requests;
        ep->rep_attr.cap.max_recv_wr += RPCRDMA_BACKWARD_WRS;
        ep->rep_attr.cap.max_recv_wr += 1;      /* drain cqe */
-       ep->rep_attr.cap.max_send_sge = RPCRDMA_MAX_IOVS;
+       ep->rep_attr.cap.max_send_sge = RPCRDMA_MAX_SEND_SGES;
        ep->rep_attr.cap.max_recv_sge = 1;
        ep->rep_attr.cap.max_inline_data = 0;
        ep->rep_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
@@ -536,9 +562,14 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
        /* Initialize cma parameters */
        memset(&ep->rep_remote_cma, 0, sizeof(ep->rep_remote_cma));
 
-       /* RPC/RDMA does not use private data */
-       ep->rep_remote_cma.private_data = NULL;
-       ep->rep_remote_cma.private_data_len = 0;
+       /* Prepare RDMA-CM private message */
+       pmsg->cp_magic = rpcrdma_cmp_magic;
+       pmsg->cp_version = RPCRDMA_CMP_VERSION;
+       pmsg->cp_flags |= ia->ri_ops->ro_send_w_inv_ok;
+       pmsg->cp_send_size = rpcrdma_encode_buffer_size(cdata->inline_wsize);
+       pmsg->cp_recv_size = rpcrdma_encode_buffer_size(cdata->inline_rsize);
+       ep->rep_remote_cma.private_data = pmsg;
+       ep->rep_remote_cma.private_data_len = sizeof(*pmsg);
 
        /* Client offers RDMA Read but does not initiate */
        ep->rep_remote_cma.initiator_depth = 0;
@@ -849,6 +880,10 @@ rpcrdma_create_req(struct rpcrdma_xprt *r_xprt)
        req->rl_cqe.done = rpcrdma_wc_send;
        req->rl_buffer = &r_xprt->rx_buf;
        INIT_LIST_HEAD(&req->rl_registered);
+       req->rl_send_wr.next = NULL;
+       req->rl_send_wr.wr_cqe = &req->rl_cqe;
+       req->rl_send_wr.sg_list = req->rl_send_sge;
+       req->rl_send_wr.opcode = IB_WR_SEND;
        return req;
 }
 
@@ -865,17 +900,21 @@ rpcrdma_create_rep(struct rpcrdma_xprt *r_xprt)
        if (rep == NULL)
                goto out;
 
-       rep->rr_rdmabuf = rpcrdma_alloc_regbuf(ia, cdata->inline_rsize,
-                                              GFP_KERNEL);
+       rep->rr_rdmabuf = rpcrdma_alloc_regbuf(cdata->inline_rsize,
+                                              DMA_FROM_DEVICE, GFP_KERNEL);
        if (IS_ERR(rep->rr_rdmabuf)) {
                rc = PTR_ERR(rep->rr_rdmabuf);
                goto out_free;
        }
 
        rep->rr_device = ia->ri_device;
-       rep->rr_cqe.done = rpcrdma_receive_wc;
+       rep->rr_cqe.done = rpcrdma_wc_receive;
        rep->rr_rxprt = r_xprt;
-       INIT_WORK(&rep->rr_work, rpcrdma_receive_worker);
+       INIT_WORK(&rep->rr_work, rpcrdma_reply_handler);
+       rep->rr_recv_wr.next = NULL;
+       rep->rr_recv_wr.wr_cqe = &rep->rr_cqe;
+       rep->rr_recv_wr.sg_list = &rep->rr_rdmabuf->rg_iov;
+       rep->rr_recv_wr.num_sge = 1;
        return rep;
 
 out_free:
@@ -966,17 +1005,18 @@ rpcrdma_buffer_get_rep_locked(struct rpcrdma_buffer *buf)
 }
 
 static void
-rpcrdma_destroy_rep(struct rpcrdma_ia *ia, struct rpcrdma_rep *rep)
+rpcrdma_destroy_rep(struct rpcrdma_rep *rep)
 {
-       rpcrdma_free_regbuf(ia, rep->rr_rdmabuf);
+       rpcrdma_free_regbuf(rep->rr_rdmabuf);
        kfree(rep);
 }
 
 void
-rpcrdma_destroy_req(struct rpcrdma_ia *ia, struct rpcrdma_req *req)
+rpcrdma_destroy_req(struct rpcrdma_req *req)
 {
-       rpcrdma_free_regbuf(ia, req->rl_sendbuf);
-       rpcrdma_free_regbuf(ia, req->rl_rdmabuf);
+       rpcrdma_free_regbuf(req->rl_recvbuf);
+       rpcrdma_free_regbuf(req->rl_sendbuf);
+       rpcrdma_free_regbuf(req->rl_rdmabuf);
        kfree(req);
 }
 
@@ -1009,15 +1049,13 @@ rpcrdma_destroy_mrs(struct rpcrdma_buffer *buf)
 void
 rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf)
 {
-       struct rpcrdma_ia *ia = rdmab_to_ia(buf);
-
        cancel_delayed_work_sync(&buf->rb_recovery_worker);
 
        while (!list_empty(&buf->rb_recv_bufs)) {
                struct rpcrdma_rep *rep;
 
                rep = rpcrdma_buffer_get_rep_locked(buf);
-               rpcrdma_destroy_rep(ia, rep);
+               rpcrdma_destroy_rep(rep);
        }
        buf->rb_send_count = 0;
 
@@ -1030,7 +1068,7 @@ rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf)
                list_del(&req->rl_all);
 
                spin_unlock(&buf->rb_reqslock);
-               rpcrdma_destroy_req(ia, req);
+               rpcrdma_destroy_req(req);
                spin_lock(&buf->rb_reqslock);
        }
        spin_unlock(&buf->rb_reqslock);
@@ -1129,7 +1167,7 @@ rpcrdma_buffer_put(struct rpcrdma_req *req)
        struct rpcrdma_buffer *buffers = req->rl_buffer;
        struct rpcrdma_rep *rep = req->rl_reply;
 
-       req->rl_niovs = 0;
+       req->rl_send_wr.num_sge = 0;
        req->rl_reply = NULL;
 
        spin_lock(&buffers->rb_lock);
@@ -1171,70 +1209,81 @@ rpcrdma_recv_buffer_put(struct rpcrdma_rep *rep)
        spin_unlock(&buffers->rb_lock);
 }
 
-/*
- * Wrappers for internal-use kmalloc memory registration, used by buffer code.
- */
-
 /**
- * rpcrdma_alloc_regbuf - kmalloc and register memory for SEND/RECV buffers
- * @ia: controlling rpcrdma_ia
+ * rpcrdma_alloc_regbuf - allocate and DMA-map memory for SEND/RECV buffers
  * @size: size of buffer to be allocated, in bytes
+ * @direction: direction of data movement
  * @flags: GFP flags
  *
- * Returns pointer to private header of an area of internally
- * registered memory, or an ERR_PTR. The registered buffer follows
- * the end of the private header.
+ * Returns an ERR_PTR, or a pointer to a regbuf, a buffer that
+ * can be persistently DMA-mapped for I/O.
  *
  * xprtrdma uses a regbuf for posting an outgoing RDMA SEND, or for
- * receiving the payload of RDMA RECV operations. regbufs are not
- * used for RDMA READ/WRITE operations, thus are registered only for
- * LOCAL access.
+ * receiving the payload of RDMA RECV operations. During Long Calls
+ * or Replies they may be registered externally via ro_map.
  */
 struct rpcrdma_regbuf *
-rpcrdma_alloc_regbuf(struct rpcrdma_ia *ia, size_t size, gfp_t flags)
+rpcrdma_alloc_regbuf(size_t size, enum dma_data_direction direction,
+                    gfp_t flags)
 {
        struct rpcrdma_regbuf *rb;
-       struct ib_sge *iov;
 
        rb = kmalloc(sizeof(*rb) + size, flags);
        if (rb == NULL)
-               goto out;
+               return ERR_PTR(-ENOMEM);
 
-       iov = &rb->rg_iov;
-       iov->addr = ib_dma_map_single(ia->ri_device,
-                                     (void *)rb->rg_base, size,
-                                     DMA_BIDIRECTIONAL);
-       if (ib_dma_mapping_error(ia->ri_device, iov->addr))
-               goto out_free;
+       rb->rg_device = NULL;
+       rb->rg_direction = direction;
+       rb->rg_iov.length = size;
 
-       iov->length = size;
-       iov->lkey = ia->ri_pd->local_dma_lkey;
-       rb->rg_size = size;
-       rb->rg_owner = NULL;
        return rb;
+}
 
-out_free:
-       kfree(rb);
-out:
-       return ERR_PTR(-ENOMEM);
+/**
+ * __rpcrdma_map_regbuf - DMA-map a regbuf
+ * @ia: controlling rpcrdma_ia
+ * @rb: regbuf to be mapped
+ */
+bool
+__rpcrdma_dma_map_regbuf(struct rpcrdma_ia *ia, struct rpcrdma_regbuf *rb)
+{
+       if (rb->rg_direction == DMA_NONE)
+               return false;
+
+       rb->rg_iov.addr = ib_dma_map_single(ia->ri_device,
+                                           (void *)rb->rg_base,
+                                           rdmab_length(rb),
+                                           rb->rg_direction);
+       if (ib_dma_mapping_error(ia->ri_device, rdmab_addr(rb)))
+               return false;
+
+       rb->rg_device = ia->ri_device;
+       rb->rg_iov.lkey = ia->ri_pd->local_dma_lkey;
+       return true;
+}
+
+static void
+rpcrdma_dma_unmap_regbuf(struct rpcrdma_regbuf *rb)
+{
+       if (!rpcrdma_regbuf_is_mapped(rb))
+               return;
+
+       ib_dma_unmap_single(rb->rg_device, rdmab_addr(rb),
+                           rdmab_length(rb), rb->rg_direction);
+       rb->rg_device = NULL;
 }
 
 /**
  * rpcrdma_free_regbuf - deregister and free registered buffer
- * @ia: controlling rpcrdma_ia
  * @rb: regbuf to be deregistered and freed
  */
 void
-rpcrdma_free_regbuf(struct rpcrdma_ia *ia, struct rpcrdma_regbuf *rb)
+rpcrdma_free_regbuf(struct rpcrdma_regbuf *rb)
 {
-       struct ib_sge *iov;
-
        if (!rb)
                return;
 
-       iov = &rb->rg_iov;
-       ib_dma_unmap_single(ia->ri_device,
-                           iov->addr, iov->length, DMA_BIDIRECTIONAL);
+       rpcrdma_dma_unmap_regbuf(rb);
        kfree(rb);
 }
 
@@ -1248,39 +1297,28 @@ rpcrdma_ep_post(struct rpcrdma_ia *ia,
                struct rpcrdma_ep *ep,
                struct rpcrdma_req *req)
 {
-       struct ib_device *device = ia->ri_device;
-       struct ib_send_wr send_wr, *send_wr_fail;
-       struct rpcrdma_rep *rep = req->rl_reply;
-       struct ib_sge *iov = req->rl_send_iov;
-       int i, rc;
+       struct ib_send_wr *send_wr = &req->rl_send_wr;
+       struct ib_send_wr *send_wr_fail;
+       int rc;
 
-       if (rep) {
-               rc = rpcrdma_ep_post_recv(ia, ep, rep);
+       if (req->rl_reply) {
+               rc = rpcrdma_ep_post_recv(ia, req->rl_reply);
                if (rc)
                        return rc;
                req->rl_reply = NULL;
        }
 
-       send_wr.next = NULL;
-       send_wr.wr_cqe = &req->rl_cqe;
-       send_wr.sg_list = iov;
-       send_wr.num_sge = req->rl_niovs;
-       send_wr.opcode = IB_WR_SEND;
-
-       for (i = 0; i < send_wr.num_sge; i++)
-               ib_dma_sync_single_for_device(device, iov[i].addr,
-                                             iov[i].length, DMA_TO_DEVICE);
        dprintk("RPC:       %s: posting %d s/g entries\n",
-               __func__, send_wr.num_sge);
+               __func__, send_wr->num_sge);
 
        if (DECR_CQCOUNT(ep) > 0)
-               send_wr.send_flags = 0;
+               send_wr->send_flags = 0;
        else { /* Provider must take a send completion every now and then */
                INIT_CQCOUNT(ep);
-               send_wr.send_flags = IB_SEND_SIGNALED;
+               send_wr->send_flags = IB_SEND_SIGNALED;
        }
 
-       rc = ib_post_send(ia->ri_id->qp, &send_wr, &send_wr_fail);
+       rc = ib_post_send(ia->ri_id->qp, send_wr, &send_wr_fail);
        if (rc)
                goto out_postsend_err;
        return 0;
@@ -1290,32 +1328,24 @@ out_postsend_err:
        return -ENOTCONN;
 }
 
-/*
- * (Re)post a receive buffer.
- */
 int
 rpcrdma_ep_post_recv(struct rpcrdma_ia *ia,
-                    struct rpcrdma_ep *ep,
                     struct rpcrdma_rep *rep)
 {
-       struct ib_recv_wr recv_wr, *recv_wr_fail;
+       struct ib_recv_wr *recv_wr_fail;
        int rc;
 
-       recv_wr.next = NULL;
-       recv_wr.wr_cqe = &rep->rr_cqe;
-       recv_wr.sg_list = &rep->rr_rdmabuf->rg_iov;
-       recv_wr.num_sge = 1;
-
-       ib_dma_sync_single_for_cpu(ia->ri_device,
-                                  rdmab_addr(rep->rr_rdmabuf),
-                                  rdmab_length(rep->rr_rdmabuf),
-                                  DMA_BIDIRECTIONAL);
-
-       rc = ib_post_recv(ia->ri_id->qp, &recv_wr, &recv_wr_fail);
+       if (!rpcrdma_dma_map_regbuf(ia, rep->rr_rdmabuf))
+               goto out_map;
+       rc = ib_post_recv(ia->ri_id->qp, &rep->rr_recv_wr, &recv_wr_fail);
        if (rc)
                goto out_postrecv;
        return 0;
 
+out_map:
+       pr_err("rpcrdma: failed to DMA map the Receive buffer\n");
+       return -EIO;
+
 out_postrecv:
        pr_err("rpcrdma: ib_post_recv returned %i\n", rc);
        return -ENOTCONN;
@@ -1333,7 +1363,6 @@ rpcrdma_ep_post_extra_recv(struct rpcrdma_xprt *r_xprt, unsigned int count)
 {
        struct rpcrdma_buffer *buffers = &r_xprt->rx_buf;
        struct rpcrdma_ia *ia = &r_xprt->rx_ia;
-       struct rpcrdma_ep *ep = &r_xprt->rx_ep;
        struct rpcrdma_rep *rep;
        int rc;
 
@@ -1344,7 +1373,7 @@ rpcrdma_ep_post_extra_recv(struct rpcrdma_xprt *r_xprt, unsigned int count)
                rep = rpcrdma_buffer_get_rep_locked(buffers);
                spin_unlock(&buffers->rb_lock);
 
-               rc = rpcrdma_ep_post_recv(ia, ep, rep);
+               rc = rpcrdma_ep_post_recv(ia, rep);
                if (rc)
                        goto out_rc;
        }
index a71b0f5897d8721ee8edaed291388c34f61d3cee..0d35b761c883d01a044ba5f9c635bb3a7edce741 100644 (file)
@@ -70,9 +70,11 @@ struct rpcrdma_ia {
        struct ib_pd            *ri_pd;
        struct completion       ri_done;
        int                     ri_async_rc;
+       unsigned int            ri_max_segs;
        unsigned int            ri_max_frmr_depth;
        unsigned int            ri_max_inline_write;
        unsigned int            ri_max_inline_read;
+       bool                    ri_reminv_expected;
        struct ib_qp_attr       ri_qp_attr;
        struct ib_qp_init_attr  ri_qp_init_attr;
 };
@@ -87,6 +89,7 @@ struct rpcrdma_ep {
        int                     rep_connected;
        struct ib_qp_init_attr  rep_attr;
        wait_queue_head_t       rep_connect_wait;
+       struct rpcrdma_connect_private  rep_cm_private;
        struct rdma_conn_param  rep_remote_cma;
        struct sockaddr_storage rep_remote_addr;
        struct delayed_work     rep_connect_worker;
@@ -112,9 +115,9 @@ struct rpcrdma_ep {
  */
 
 struct rpcrdma_regbuf {
-       size_t                  rg_size;
-       struct rpcrdma_req      *rg_owner;
        struct ib_sge           rg_iov;
+       struct ib_device        *rg_device;
+       enum dma_data_direction rg_direction;
        __be32                  rg_base[0] __attribute__ ((aligned(256)));
 };
 
@@ -162,7 +165,10 @@ rdmab_to_msg(struct rpcrdma_regbuf *rb)
  * The smallest inline threshold is 1024 bytes, ensuring that
  * at least 750 bytes are available for RPC messages.
  */
-#define RPCRDMA_MAX_HDR_SEGS   (8)
+enum {
+       RPCRDMA_MAX_HDR_SEGS = 8,
+       RPCRDMA_HDRBUF_SIZE = 256,
+};
 
 /*
  * struct rpcrdma_rep -- this structure encapsulates state required to recv
@@ -182,10 +188,13 @@ rdmab_to_msg(struct rpcrdma_regbuf *rb)
 struct rpcrdma_rep {
        struct ib_cqe           rr_cqe;
        unsigned int            rr_len;
+       int                     rr_wc_flags;
+       u32                     rr_inv_rkey;
        struct ib_device        *rr_device;
        struct rpcrdma_xprt     *rr_rxprt;
        struct work_struct      rr_work;
        struct list_head        rr_list;
+       struct ib_recv_wr       rr_recv_wr;
        struct rpcrdma_regbuf   *rr_rdmabuf;
 };
 
@@ -276,19 +285,30 @@ struct rpcrdma_mr_seg {           /* chunk descriptors */
        char            *mr_offset;     /* kva if no page, else offset */
 };
 
-#define RPCRDMA_MAX_IOVS       (2)
+/* Reserve enough Send SGEs to send a maximum size inline request:
+ * - RPC-over-RDMA header
+ * - xdr_buf head iovec
+ * - RPCRDMA_MAX_INLINE bytes, possibly unaligned, in pages
+ * - xdr_buf tail iovec
+ */
+enum {
+       RPCRDMA_MAX_SEND_PAGES = PAGE_SIZE + RPCRDMA_MAX_INLINE - 1,
+       RPCRDMA_MAX_PAGE_SGES = (RPCRDMA_MAX_SEND_PAGES >> PAGE_SHIFT) + 1,
+       RPCRDMA_MAX_SEND_SGES = 1 + 1 + RPCRDMA_MAX_PAGE_SGES + 1,
+};
 
 struct rpcrdma_buffer;
 struct rpcrdma_req {
        struct list_head        rl_free;
-       unsigned int            rl_niovs;
+       unsigned int            rl_mapped_sges;
        unsigned int            rl_connect_cookie;
-       struct rpc_task         *rl_task;
        struct rpcrdma_buffer   *rl_buffer;
-       struct rpcrdma_rep      *rl_reply;/* holder for reply buffer */
-       struct ib_sge           rl_send_iov[RPCRDMA_MAX_IOVS];
-       struct rpcrdma_regbuf   *rl_rdmabuf;
-       struct rpcrdma_regbuf   *rl_sendbuf;
+       struct rpcrdma_rep      *rl_reply;
+       struct ib_send_wr       rl_send_wr;
+       struct ib_sge           rl_send_sge[RPCRDMA_MAX_SEND_SGES];
+       struct rpcrdma_regbuf   *rl_rdmabuf;    /* xprt header */
+       struct rpcrdma_regbuf   *rl_sendbuf;    /* rq_snd_buf */
+       struct rpcrdma_regbuf   *rl_recvbuf;    /* rq_rcv_buf */
 
        struct ib_cqe           rl_cqe;
        struct list_head        rl_all;
@@ -298,14 +318,16 @@ struct rpcrdma_req {
        struct rpcrdma_mr_seg   rl_segments[RPCRDMA_MAX_SEGS];
 };
 
+static inline void
+rpcrdma_set_xprtdata(struct rpc_rqst *rqst, struct rpcrdma_req *req)
+{
+       rqst->rq_xprtdata = req;
+}
+
 static inline struct rpcrdma_req *
 rpcr_to_rdmar(struct rpc_rqst *rqst)
 {
-       void *buffer = rqst->rq_buffer;
-       struct rpcrdma_regbuf *rb;
-
-       rb = container_of(buffer, struct rpcrdma_regbuf, rg_base);
-       return rb->rg_owner;
+       return rqst->rq_xprtdata;
 }
 
 /*
@@ -356,15 +378,6 @@ struct rpcrdma_create_data_internal {
        unsigned int    padding;        /* non-rdma write header padding */
 };
 
-#define RPCRDMA_INLINE_READ_THRESHOLD(rq) \
-       (rpcx_to_rdmad(rq->rq_xprt).inline_rsize)
-
-#define RPCRDMA_INLINE_WRITE_THRESHOLD(rq)\
-       (rpcx_to_rdmad(rq->rq_xprt).inline_wsize)
-
-#define RPCRDMA_INLINE_PAD_VALUE(rq)\
-       rpcx_to_rdmad(rq->rq_xprt).padding
-
 /*
  * Statistics for RPCRDMA
  */
@@ -386,6 +399,7 @@ struct rpcrdma_stats {
        unsigned long           mrs_recovered;
        unsigned long           mrs_orphaned;
        unsigned long           mrs_allocated;
+       unsigned long           local_inv_needed;
 };
 
 /*
@@ -409,6 +423,7 @@ struct rpcrdma_memreg_ops {
                                      struct rpcrdma_mw *);
        void            (*ro_release_mr)(struct rpcrdma_mw *);
        const char      *ro_displayname;
+       const int       ro_send_w_inv_ok;
 };
 
 extern const struct rpcrdma_memreg_ops rpcrdma_fmr_memreg_ops;
@@ -461,15 +476,14 @@ void rpcrdma_ep_disconnect(struct rpcrdma_ep *, struct rpcrdma_ia *);
 
 int rpcrdma_ep_post(struct rpcrdma_ia *, struct rpcrdma_ep *,
                                struct rpcrdma_req *);
-int rpcrdma_ep_post_recv(struct rpcrdma_ia *, struct rpcrdma_ep *,
-                               struct rpcrdma_rep *);
+int rpcrdma_ep_post_recv(struct rpcrdma_ia *, struct rpcrdma_rep *);
 
 /*
  * Buffer calls - xprtrdma/verbs.c
  */
 struct rpcrdma_req *rpcrdma_create_req(struct rpcrdma_xprt *);
 struct rpcrdma_rep *rpcrdma_create_rep(struct rpcrdma_xprt *);
-void rpcrdma_destroy_req(struct rpcrdma_ia *, struct rpcrdma_req *);
+void rpcrdma_destroy_req(struct rpcrdma_req *);
 int rpcrdma_buffer_create(struct rpcrdma_xprt *);
 void rpcrdma_buffer_destroy(struct rpcrdma_buffer *);
 
@@ -482,10 +496,24 @@ void rpcrdma_recv_buffer_put(struct rpcrdma_rep *);
 
 void rpcrdma_defer_mr_recovery(struct rpcrdma_mw *);
 
-struct rpcrdma_regbuf *rpcrdma_alloc_regbuf(struct rpcrdma_ia *,
-                                           size_t, gfp_t);
-void rpcrdma_free_regbuf(struct rpcrdma_ia *,
-                        struct rpcrdma_regbuf *);
+struct rpcrdma_regbuf *rpcrdma_alloc_regbuf(size_t, enum dma_data_direction,
+                                           gfp_t);
+bool __rpcrdma_dma_map_regbuf(struct rpcrdma_ia *, struct rpcrdma_regbuf *);
+void rpcrdma_free_regbuf(struct rpcrdma_regbuf *);
+
+static inline bool
+rpcrdma_regbuf_is_mapped(struct rpcrdma_regbuf *rb)
+{
+       return rb->rg_device != NULL;
+}
+
+static inline bool
+rpcrdma_dma_map_regbuf(struct rpcrdma_ia *ia, struct rpcrdma_regbuf *rb)
+{
+       if (likely(rpcrdma_regbuf_is_mapped(rb)))
+               return true;
+       return __rpcrdma_dma_map_regbuf(ia, rb);
+}
 
 int rpcrdma_ep_post_extra_recv(struct rpcrdma_xprt *, unsigned int);
 
@@ -507,15 +535,25 @@ rpcrdma_data_dir(bool writing)
  */
 void rpcrdma_connect_worker(struct work_struct *);
 void rpcrdma_conn_func(struct rpcrdma_ep *);
-void rpcrdma_reply_handler(struct rpcrdma_rep *);
+void rpcrdma_reply_handler(struct work_struct *);
 
 /*
  * RPC/RDMA protocol calls - xprtrdma/rpc_rdma.c
  */
+
+enum rpcrdma_chunktype {
+       rpcrdma_noch = 0,
+       rpcrdma_readch,
+       rpcrdma_areadch,
+       rpcrdma_writech,
+       rpcrdma_replych
+};
+
+bool rpcrdma_prepare_send_sges(struct rpcrdma_ia *, struct rpcrdma_req *,
+                              u32, struct xdr_buf *, enum rpcrdma_chunktype);
+void rpcrdma_unmap_sges(struct rpcrdma_ia *, struct rpcrdma_req *);
 int rpcrdma_marshal_req(struct rpc_rqst *);
-void rpcrdma_set_max_header_sizes(struct rpcrdma_ia *,
-                                 struct rpcrdma_create_data_internal *,
-                                 unsigned int);
+void rpcrdma_set_max_header_sizes(struct rpcrdma_xprt *);
 
 /* RPC/RDMA module init - xprtrdma/transport.c
  */
index bf168838a0296e9387de33ad5afabb3bdf3b35f0..0137af1c0916cdf25547f89ed20b62588fc5c0b2 100644 (file)
@@ -473,7 +473,16 @@ static int xs_nospace(struct rpc_task *task)
        spin_unlock_bh(&xprt->transport_lock);
 
        /* Race breaker in case memory is freed before above code is called */
-       sk->sk_write_space(sk);
+       if (ret == -EAGAIN) {
+               struct socket_wq *wq;
+
+               rcu_read_lock();
+               wq = rcu_dereference(sk->sk_wq);
+               set_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags);
+               rcu_read_unlock();
+
+               sk->sk_write_space(sk);
+       }
        return ret;
 }
 
@@ -2533,35 +2542,38 @@ static void xs_tcp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
  * we allocate pages instead doing a kmalloc like rpc_malloc is because we want
  * to use the server side send routines.
  */
-static void *bc_malloc(struct rpc_task *task, size_t size)
+static int bc_malloc(struct rpc_task *task)
 {
+       struct rpc_rqst *rqst = task->tk_rqstp;
+       size_t size = rqst->rq_callsize;
        struct page *page;
        struct rpc_buffer *buf;
 
-       WARN_ON_ONCE(size > PAGE_SIZE - sizeof(struct rpc_buffer));
-       if (size > PAGE_SIZE - sizeof(struct rpc_buffer))
-               return NULL;
+       if (size > PAGE_SIZE - sizeof(struct rpc_buffer)) {
+               WARN_ONCE(1, "xprtsock: large bc buffer request (size %zu)\n",
+                         size);
+               return -EINVAL;
+       }
 
        page = alloc_page(GFP_KERNEL);
        if (!page)
-               return NULL;
+               return -ENOMEM;
 
        buf = page_address(page);
        buf->len = PAGE_SIZE;
 
-       return buf->data;
+       rqst->rq_buffer = buf->data;
+       return 0;
 }
 
 /*
  * Free the space allocated in the bc_alloc routine
  */
-static void bc_free(void *buffer)
+static void bc_free(struct rpc_task *task)
 {
+       void *buffer = task->tk_rqstp->rq_buffer;
        struct rpc_buffer *buf;
 
-       if (!buffer)
-               return;
-
        buf = container_of(buffer, struct rpc_buffer, data);
        free_page((unsigned long)buf);
 }
index d80cd3f7503f26b9fc3f6171c125152ac332115b..78cab9c5a445afd5a57a9b53d6960da533a12ea0 100644 (file)
@@ -407,6 +407,7 @@ static int __tipc_nl_add_udp_addr(struct sk_buff *skb,
        if (ntohs(addr->proto) == ETH_P_IP) {
                struct sockaddr_in ip4;
 
+               memset(&ip4, 0, sizeof(ip4));
                ip4.sin_family = AF_INET;
                ip4.sin_port = addr->port;
                ip4.sin_addr.s_addr = addr->ipv4.s_addr;
@@ -417,6 +418,7 @@ static int __tipc_nl_add_udp_addr(struct sk_buff *skb,
        } else if (ntohs(addr->proto) == ETH_P_IPV6) {
                struct sockaddr_in6 ip6;
 
+               memset(&ip6, 0, sizeof(ip6));
                ip6.sin6_family = AF_INET6;
                ip6.sin6_port  = addr->port;
                memcpy(&ip6.sin6_addr, &addr->ipv6, sizeof(struct in6_addr));
index 85c405fcccb0cb08ec59efa262961e08bb3389f1..a6d2a43bbf2e290368410a6338abefecfa114f54 100644 (file)
@@ -99,4 +99,10 @@ config SAMPLE_SECCOMP
          Build samples of seccomp filters using various methods of
          BPF filter construction.
 
+config SAMPLE_BLACKFIN_GPTIMERS
+       tristate "Build blackfin gptimers sample code -- loadable modules only"
+       depends on BLACKFIN && BFIN_GPTIMERS && m
+       help
+         Build samples of blackfin gptimers sample module.
+
 endif # SAMPLES
index 1a20169d85acf9a6e54cdaa0cd83650ac53b7d0c..e17d66d77f099c48f88bdc8518ca34c45b39cf29 100644 (file)
@@ -2,4 +2,4 @@
 
 obj-$(CONFIG_SAMPLES)  += kobject/ kprobes/ trace_events/ livepatch/ \
                           hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \
-                          configfs/ connector/ v4l/ trace_printk/
+                          configfs/ connector/ v4l/ trace_printk/ blackfin/
diff --git a/samples/auxdisplay/Makefile b/samples/auxdisplay/Makefile
new file mode 100644 (file)
index 0000000..05e471f
--- /dev/null
@@ -0,0 +1,9 @@
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -I../../usr/include
+
+PROGS := cfag12864b-example
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
diff --git a/samples/blackfin/Makefile b/samples/blackfin/Makefile
new file mode 100644 (file)
index 0000000..89b86cf
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_SAMPLE_BLACKFIN_GPTIMERS) += gptimers-example.o
diff --git a/samples/mei/Makefile b/samples/mei/Makefile
new file mode 100644 (file)
index 0000000..7aac216
--- /dev/null
@@ -0,0 +1,9 @@
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -I../../usr/include
+
+PROGS := mei-amt-version
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
diff --git a/samples/mic/mpssd/Makefile b/samples/mic/mpssd/Makefile
new file mode 100644 (file)
index 0000000..3e3ef91
--- /dev/null
@@ -0,0 +1,27 @@
+ifndef CROSS_COMPILE
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
+
+ifeq ($(ARCH),x86)
+
+PROGS := mpssd
+CC = $(CROSS_COMPILE)gcc
+CFLAGS := -I../../../usr/include -I../../../tools/include
+
+ifdef DEBUG
+CFLAGS += -DDEBUG=$(DEBUG)
+endif
+
+all: $(PROGS)
+mpssd: mpssd.c sysfs.c
+       $(CC) $(CFLAGS) mpssd.c sysfs.c -o mpssd -lpthread
+
+install:
+       install mpssd /usr/sbin/mpssd
+       install micctrl /usr/sbin/micctrl
+
+clean:
+       rm -fr $(PROGS)
+
+endif
+endif
diff --git a/samples/timers/Makefile b/samples/timers/Makefile
new file mode 100644 (file)
index 0000000..a5c3c4a
--- /dev/null
@@ -0,0 +1,15 @@
+ifndef CROSS_COMPILE
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
+
+ifeq ($(ARCH),x86)
+CC := $(CROSS_COMPILE)gcc
+PROGS := hpet_example
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
+
+endif
+endif
similarity index 53%
rename from Documentation/watchdog/src/.gitignore
rename to samples/watchdog/.gitignore
index ac90997dba937295f0059b93c2c146cb0e6ec00c..ff0ebb54033341c8235e10a88b0cc858e5388f91 100644 (file)
@@ -1,2 +1 @@
 watchdog-simple
-watchdog-test
diff --git a/samples/watchdog/Makefile b/samples/watchdog/Makefile
new file mode 100644 (file)
index 0000000..9b53d89
--- /dev/null
@@ -0,0 +1,8 @@
+CC := $(CROSS_COMPILE)gcc
+PROGS := watchdog-simple
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
+
index 11602e5efb3bcdf1d300315a4501b8ce3faaa78c..de46ab03f063beaf980996102e0ec8911e173099 100644 (file)
@@ -81,6 +81,7 @@ endif
 
 ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
 lib-target := $(obj)/lib.a
+obj-y += $(obj)/lib-ksyms.o
 endif
 
 ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
@@ -358,12 +359,22 @@ $(sort $(subdir-obj-y)): $(subdir-ym) ;
 # Rule to compile a set of .o files into one .o file
 #
 ifdef builtin-target
-quiet_cmd_link_o_target = LD      $@
+
+ifdef CONFIG_THIN_ARCHIVES
+  cmd_make_builtin = rm -f $@; $(AR) rcST$(KBUILD_ARFLAGS)
+  cmd_make_empty_builtin = rm -f $@; $(AR) rcST$(KBUILD_ARFLAGS)
+  quiet_cmd_link_o_target = AR      $@
+else
+  cmd_make_builtin = $(LD) $(ld_flags) -r -o
+  cmd_make_empty_builtin = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS)
+  quiet_cmd_link_o_target = LD      $@
+endif
+
 # If the list of objects to link is empty, just create an empty built-in.o
 cmd_link_o_target = $(if $(strip $(obj-y)),\
-                     $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
+                     $(cmd_make_builtin) $@ $(filter $(obj-y), $^) \
                      $(cmd_secanalysis),\
-                     rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@)
+                     $(cmd_make_empty_builtin) $@)
 
 $(builtin-target): $(obj-y) FORCE
        $(call if_changed,link_o_target)
@@ -389,12 +400,36 @@ $(modorder-target): $(subdir-ym) FORCE
 #
 ifdef lib-target
 quiet_cmd_link_l_target = AR      $@
-cmd_link_l_target = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@ $(lib-y)
+
+ifdef CONFIG_THIN_ARCHIVES
+  cmd_link_l_target = rm -f $@; $(AR) rcsT$(KBUILD_ARFLAGS) $@ $(lib-y)
+else
+  cmd_link_l_target = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@ $(lib-y)
+endif
 
 $(lib-target): $(lib-y) FORCE
        $(call if_changed,link_l_target)
 
 targets += $(lib-target)
+
+dummy-object = $(obj)/.lib_exports.o
+ksyms-lds = $(dot-target).lds
+ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX
+ref_prefix = EXTERN(_
+else
+ref_prefix = EXTERN(
+endif
+
+quiet_cmd_export_list = EXPORTS $@
+cmd_export_list = $(OBJDUMP) -h $< | \
+       sed -ne '/___ksymtab/{s/.*+/$(ref_prefix)/;s/ .*/)/;p}' >$(ksyms-lds);\
+       rm -f $(dummy-object);\
+       $(AR) rcs$(KBUILD_ARFLAGS) $(dummy-object);\
+       $(LD) $(ld_flags) -r -o $@ -T $(ksyms-lds) $(dummy-object);\
+       rm $(dummy-object) $(ksyms-lds)
+
+$(obj)/lib-ksyms.o: $(lib-target) FORCE
+       $(call if_changed,export_list)
 endif
 
 #
index 61f0e6db909bbf0a7016d60f4b58a4c4bd3c0dba..060d2cb373dbe3fe788b7ad01b7868eca3b73166 100644 (file)
@@ -6,6 +6,12 @@ ifdef CONFIG_GCC_PLUGINS
 
   gcc-plugin-$(CONFIG_GCC_PLUGIN_CYC_COMPLEXITY)       += cyc_complexity_plugin.so
 
+  gcc-plugin-$(CONFIG_GCC_PLUGIN_LATENT_ENTROPY)       += latent_entropy_plugin.so
+  gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_LATENT_ENTROPY)        += -DLATENT_ENTROPY_PLUGIN
+  ifdef CONFIG_PAX_LATENT_ENTROPY
+    DISABLE_LATENT_ENTROPY_PLUGIN                      += -fplugin-arg-latent_entropy_plugin-disable
+  endif
+
   ifdef CONFIG_GCC_PLUGIN_SANCOV
     ifeq ($(CFLAGS_KCOV),)
       # It is needed because of the gcc-plugin.sh and gcc version checks.
@@ -21,7 +27,8 @@ ifdef CONFIG_GCC_PLUGINS
 
   GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
 
-  export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR SANCOV_PLUGIN
+  export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
+  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
 
   ifneq ($(PLUGINCC),)
     # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
index 1366a94b6c39565f094f2907e61a21464e0897fe..16923ba4b5b1005158508b4e0c9d2b3d947c119c 100644 (file)
@@ -115,14 +115,18 @@ $(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE
 
 targets += $(modules:.ko=.mod.o)
 
-# Step 6), final link of the modules
+ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
+
+# Step 6), final link of the modules with optional arch pass after final link
 quiet_cmd_ld_ko_o = LD [M]  $@
-      cmd_ld_ko_o = $(LD) -r $(LDFLAGS)                                 \
-                             $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
-                             -o $@ $(filter-out FORCE,$^)
+      cmd_ld_ko_o =                                                     \
+       $(LD) -r $(LDFLAGS)                                             \
+                 $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE)             \
+                 -o $@ $(filter-out FORCE,$^) ;                         \
+       $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
 
 $(modules): %.ko :%.o %.mod.o FORCE
-       $(call if_changed,ld_ko_o)
+       +$(call if_changed,ld_ko_o)
 
 targets += $(modules)
 
index 746ec1ece6143fab9eb4a667f521c19fbabdc67c..fff818b92acb7e3e469ce06cc722f3ab0f275171 100644 (file)
@@ -82,8 +82,7 @@
  * to date before even starting the recursive build, so it's too late
  * at this point anyway.
  *
- * The algorithm to grep for "CONFIG_..." is bit unusual, but should
- * be fast ;-) We don't even try to really parse the header files, but
+ * We don't even try to really parse the header files, but
  * merely grep, i.e. if CONFIG_FOO is mentioned in a comment, it will
  * be picked up as well. It's not a problem with respect to
  * correctness, since that can only give too many dependencies, thus
 #include <ctype.h>
 #include <arpa/inet.h>
 
-#define INT_CONF ntohl(0x434f4e46)
-#define INT_ONFI ntohl(0x4f4e4649)
-#define INT_NFIG ntohl(0x4e464947)
-#define INT_FIG_ ntohl(0x4649475f)
-
 int insert_extra_deps;
 char *target;
 char *depfile;
@@ -241,37 +235,22 @@ static void use_config(const char *m, int slen)
        print_config(m, slen);
 }
 
-static void parse_config_file(const char *map, size_t len)
+static void parse_config_file(const char *p)
 {
-       const int *end = (const int *) (map + len);
-       /* start at +1, so that p can never be < map */
-       const int *m   = (const int *) map + 1;
-       const char *p, *q;
-
-       for (; m < end; m++) {
-               if (*m == INT_CONF) { p = (char *) m  ; goto conf; }
-               if (*m == INT_ONFI) { p = (char *) m-1; goto conf; }
-               if (*m == INT_NFIG) { p = (char *) m-2; goto conf; }
-               if (*m == INT_FIG_) { p = (char *) m-3; goto conf; }
-               continue;
-       conf:
-               if (p > map + len - 7)
-                       continue;
-               if (memcmp(p, "CONFIG_", 7))
-                       continue;
+       const char *q, *r;
+
+       while ((p = strstr(p, "CONFIG_"))) {
                p += 7;
-               for (q = p; q < map + len; q++) {
-                       if (!(isalnum(*q) || *q == '_'))
-                               goto found;
-               }
-               continue;
-
-       found:
-               if (!memcmp(q - 7, "_MODULE", 7))
-                       q -= 7;
-               if (q - p < 0)
-                       continue;
-               use_config(p, q - p);
+               q = p;
+               while (*q && (isalnum(*q) || *q == '_'))
+                       q++;
+               if (memcmp(q - 7, "_MODULE", 7) == 0)
+                       r = q - 7;
+               else
+                       r = q;
+               if (r > p)
+                       use_config(p, r - p);
+               p = q;
        }
 }
 
@@ -291,7 +270,7 @@ static void do_config_file(const char *filename)
 {
        struct stat st;
        int fd;
-       void *map;
+       char *map;
 
        fd = open(filename, O_RDONLY);
        if (fd < 0) {
@@ -308,18 +287,23 @@ static void do_config_file(const char *filename)
                close(fd);
                return;
        }
-       map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-       if ((long) map == -1) {
-               perror("fixdep: mmap");
+       map = malloc(st.st_size + 1);
+       if (!map) {
+               perror("fixdep: malloc");
                close(fd);
                return;
        }
+       if (read(fd, map, st.st_size) != st.st_size) {
+               perror("fixdep: read");
+               close(fd);
+               return;
+       }
+       map[st.st_size] = '\0';
+       close(fd);
 
-       parse_config_file(map, st.st_size);
-
-       munmap(map, st.st_size);
+       parse_config_file(map);
 
-       close(fd);
+       free(map);
 }
 
 /*
@@ -446,22 +430,8 @@ static void print_deps(void)
        close(fd);
 }
 
-static void traps(void)
-{
-       static char test[] __attribute__((aligned(sizeof(int)))) = "CONF";
-       int *p = (int *)test;
-
-       if (*p != INT_CONF) {
-               fprintf(stderr, "fixdep: sizeof(int) != 4 or wrong endianness? %#x\n",
-                       *p);
-               exit(2);
-       }
-}
-
 int main(int argc, char *argv[])
 {
-       traps();
-
        if (argc == 5 && !strcmp(argv[1], "-e")) {
                insert_extra_deps = 1;
                argv++;
index c92c1528a54dd54589878df99c2d9bb8dd4c09aa..ec487b8e7051e4c0cee49a800a3d63223a7acf9c 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/bash
 # Linux kernel coccicheck
 #
-# Read Documentation/coccinelle.txt
+# Read Documentation/dev-tools/coccinelle.rst
 #
 # This script requires at least spatch
 # version 1.0.0-rc11.
index c606231b0e4695683327bdd92132879dd1113bbe..2a5aea8e8487cdde856b18a948deb91241ea6bc6 100644 (file)
@@ -15,11 +15,11 @@ virtual org
 virtual report
 
 @depends on patch@
-expression from,to,size,flag;
+expression from,to,size;
 identifier l1,l2;
 @@
 
--  to = \(kmalloc\|kzalloc\)(size,flag);
+-  to = \(kmalloc\|kzalloc\)(size,GFP_KERNEL);
 +  to = memdup_user(from,size);
    if (
 -      to==NULL
@@ -37,12 +37,12 @@ identifier l1,l2;
 -  }
 
 @r depends on !patch@
-expression from,to,size,flag;
+expression from,to,size;
 position p;
 statement S1,S2;
 @@
 
-*  to = \(kmalloc@p\|kzalloc@p\)(size,flag);
+*  to = \(kmalloc@p\|kzalloc@p\)(size,GFP_KERNEL);
    if (to==NULL || ...) S1
    if (copy_from_user(to, from, size) != 0)
    S2
index 89b98a2f7a6f46d74863701ed49f8f60455731ed..d67ccf5f822797928151bf3d659d3e28661e4c3a 100644 (file)
@@ -17,9 +17,10 @@ virtual report
 
 @runtime_bad_err_handle exists@
 expression ret;
+position p;
 @@
 (
-ret = \(pm_runtime_idle\|
+ret@p = \(pm_runtime_idle\|
        pm_runtime_suspend\|
        pm_runtime_autosuspend\|
        pm_runtime_resume\|
@@ -47,12 +48,13 @@ IS_ERR_VALUE(ret)
 //  For context mode
 //----------------------------------------------------------
 
-@depends on runtime_bad_err_handle && context@
+@depends on context@
 identifier pm_runtime_api;
 expression ret;
+position runtime_bad_err_handle.p;
 @@
 (
-ret = pm_runtime_api(...);
+ret@p = pm_runtime_api(...);
 ...
 * IS_ERR_VALUE(ret)
 ...
@@ -62,12 +64,13 @@ ret = pm_runtime_api(...);
 //  For patch mode
 //----------------------------------------------------------
 
-@depends on runtime_bad_err_handle && patch@
+@depends on patch@
 identifier pm_runtime_api;
 expression ret;
+position runtime_bad_err_handle.p;
 @@
 (
-ret = pm_runtime_api(...);
+ret@p = pm_runtime_api(...);
 ...
 - IS_ERR_VALUE(ret)
 + ret < 0
@@ -78,13 +81,14 @@ ret = pm_runtime_api(...);
 //  For org and report mode
 //----------------------------------------------------------
 
-@r depends on runtime_bad_err_handle && (org || report) exists@
+@r depends on (org || report) exists@
 position p1, p2;
 identifier pm_runtime_api;
 expression ret;
+position runtime_bad_err_handle.p;
 @@
 (
-ret = pm_runtime_api@p1(...);
+ret@p = pm_runtime_api@p1(...);
 ...
 IS_ERR_VALUE@p2(ret)
 ...
diff --git a/scripts/coccinelle/misc/cond_no_effect.cocci b/scripts/coccinelle/misc/cond_no_effect.cocci
new file mode 100644 (file)
index 0000000..8467dbd
--- /dev/null
@@ -0,0 +1,64 @@
+///Find conditions where if and else branch are functionally
+// identical.
+//
+// There can be false positives in cases where the positional
+// information is used (as with lockdep) or where the identity
+// is a placeholder for not yet handled cases.
+// Unfortunately there also seems to be a tendency to use
+// the last if else/else as a "default behavior" - which some
+// might consider a legitimate coding pattern. From discussion
+// on kernelnewbies though it seems that this is not really an
+// accepted pattern and if at all it would need to be commented
+//
+// In the Linux kernel it does not seem to actually report
+// false positives except for those that were documented as
+// being intentional.
+// the two known cases are:
+//   arch/sh/kernel/traps_64.c:read_opcode()
+//        } else if ((pc & 1) == 0) {
+//              /* SHcompact */
+//              /* TODO : provide handling for this.  We don't really support
+//                 user-mode SHcompact yet, and for a kernel fault, this would
+//                 have to come from a module built for SHcompact.  */
+//              return -EFAULT;
+//      } else {
+//              /* misaligned */
+//              return -EFAULT;
+//      }
+//   fs/kernfs/file.c:kernfs_fop_open()
+//       * Both paths of the branch look the same.  They're supposed to
+//       * look that way and give @of->mutex different static lockdep keys.
+//       */
+//      if (has_mmap)
+//              mutex_init(&of->mutex);
+//      else
+//              mutex_init(&of->mutex);
+//
+// All other cases look like bugs or at least lack of documentation
+//
+// Confidence: Moderate
+// Copyright: (C) 2016 Nicholas Mc Guire, OSADL.  GPLv2.
+// Comments:
+// Options: --no-includes --include-headers
+
+virtual org
+virtual report
+
+@cond@
+statement S1;
+position p;
+@@
+
+* if@p (...) S1 else S1
+
+@script:python depends on org@
+p << cond.p;
+@@
+
+cocci.print_main("WARNING: possible condition with no effect (if == else)",p)
+
+@script:python depends on report@
+p << cond.p;
+@@
+
+coccilib.report.print_report(p[0],"WARNING: possible condition with no effect (if == else)")
diff --git a/scripts/gcc-plugins/latent_entropy_plugin.c b/scripts/gcc-plugins/latent_entropy_plugin.c
new file mode 100644 (file)
index 0000000..ff1939b
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+ * Copyright 2012-2016 by the PaX Team <pageexec@freemail.hu>
+ * Copyright 2016 by Emese Revfy <re.emese@gmail.com>
+ * Licensed under the GPL v2
+ *
+ * Note: the choice of the license means that the compilation process is
+ *       NOT 'eligible' as defined by gcc's library exception to the GPL v3,
+ *       but for the kernel it doesn't matter since it doesn't link against
+ *       any of the gcc libraries
+ *
+ * This gcc plugin helps generate a little bit of entropy from program state,
+ * used throughout the uptime of the kernel. Here is an instrumentation example:
+ *
+ * before:
+ * void __latent_entropy test(int argc, char *argv[])
+ * {
+ *     if (argc <= 1)
+ *             printf("%s: no command arguments :(\n", *argv);
+ *     else
+ *             printf("%s: %d command arguments!\n", *argv, args - 1);
+ * }
+ *
+ * after:
+ * void __latent_entropy test(int argc, char *argv[])
+ * {
+ *     // latent_entropy_execute() 1.
+ *     unsigned long local_entropy;
+ *     // init_local_entropy() 1.
+ *     void *local_entropy_frameaddr;
+ *     // init_local_entropy() 3.
+ *     unsigned long tmp_latent_entropy;
+ *
+ *     // init_local_entropy() 2.
+ *     local_entropy_frameaddr = __builtin_frame_address(0);
+ *     local_entropy = (unsigned long) local_entropy_frameaddr;
+ *
+ *     // init_local_entropy() 4.
+ *     tmp_latent_entropy = latent_entropy;
+ *     // init_local_entropy() 5.
+ *     local_entropy ^= tmp_latent_entropy;
+ *
+ *     // latent_entropy_execute() 3.
+ *     if (argc <= 1) {
+ *             // perturb_local_entropy()
+ *             local_entropy += 4623067384293424948;
+ *             printf("%s: no command arguments :(\n", *argv);
+ *             // perturb_local_entropy()
+ *     } else {
+ *             local_entropy ^= 3896280633962944730;
+ *             printf("%s: %d command arguments!\n", *argv, args - 1);
+ *     }
+ *
+ *     // latent_entropy_execute() 4.
+ *     tmp_latent_entropy = rol(tmp_latent_entropy, local_entropy);
+ *     latent_entropy = tmp_latent_entropy;
+ * }
+ *
+ * TODO:
+ * - add ipa pass to identify not explicitly marked candidate functions
+ * - mix in more program state (function arguments/return values,
+ *   loop variables, etc)
+ * - more instrumentation control via attribute parameters
+ *
+ * BUGS:
+ * - none known
+ *
+ * Options:
+ * -fplugin-arg-latent_entropy_plugin-disable
+ *
+ * Attribute: __attribute__((latent_entropy))
+ *  The latent_entropy gcc attribute can be only on functions and variables.
+ *  If it is on a function then the plugin will instrument it. If the attribute
+ *  is on a variable then the plugin will initialize it with a random value.
+ *  The variable must be an integer, an integer array type or a structure
+ *  with integer fields.
+ */
+
+#include "gcc-common.h"
+
+int plugin_is_GPL_compatible;
+
+static GTY(()) tree latent_entropy_decl;
+
+static struct plugin_info latent_entropy_plugin_info = {
+       .version        = "201606141920vanilla",
+       .help           = "disable\tturn off latent entropy instrumentation\n",
+};
+
+static unsigned HOST_WIDE_INT seed;
+/*
+ * get_random_seed() (this is a GCC function) generates the seed.
+ * This is a simple random generator without any cryptographic security because
+ * the entropy doesn't come from here.
+ */
+static unsigned HOST_WIDE_INT get_random_const(void)
+{
+       unsigned int i;
+       unsigned HOST_WIDE_INT ret = 0;
+
+       for (i = 0; i < 8 * sizeof(ret); i++) {
+               ret = (ret << 1) | (seed & 1);
+               seed >>= 1;
+               if (ret & 1)
+                       seed ^= 0xD800000000000000ULL;
+       }
+
+       return ret;
+}
+
+static tree tree_get_random_const(tree type)
+{
+       unsigned long long mask;
+
+       mask = 1ULL << (TREE_INT_CST_LOW(TYPE_SIZE(type)) - 1);
+       mask = 2 * (mask - 1) + 1;
+
+       if (TYPE_UNSIGNED(type))
+               return build_int_cstu(type, mask & get_random_const());
+       return build_int_cst(type, mask & get_random_const());
+}
+
+static tree handle_latent_entropy_attribute(tree *node, tree name,
+                                               tree args __unused,
+                                               int flags __unused,
+                                               bool *no_add_attrs)
+{
+       tree type;
+#if BUILDING_GCC_VERSION <= 4007
+       VEC(constructor_elt, gc) *vals;
+#else
+       vec<constructor_elt, va_gc> *vals;
+#endif
+
+       switch (TREE_CODE(*node)) {
+       default:
+               *no_add_attrs = true;
+               error("%qE attribute only applies to functions and variables",
+                       name);
+               break;
+
+       case VAR_DECL:
+               if (DECL_INITIAL(*node)) {
+                       *no_add_attrs = true;
+                       error("variable %qD with %qE attribute must not be initialized",
+                               *node, name);
+                       break;
+               }
+
+               if (!TREE_STATIC(*node)) {
+                       *no_add_attrs = true;
+                       error("variable %qD with %qE attribute must not be local",
+                               *node, name);
+                       break;
+               }
+
+               type = TREE_TYPE(*node);
+               switch (TREE_CODE(type)) {
+               default:
+                       *no_add_attrs = true;
+                       error("variable %qD with %qE attribute must be an integer or a fixed length integer array type or a fixed sized structure with integer fields",
+                               *node, name);
+                       break;
+
+               case RECORD_TYPE: {
+                       tree fld, lst = TYPE_FIELDS(type);
+                       unsigned int nelt = 0;
+
+                       for (fld = lst; fld; nelt++, fld = TREE_CHAIN(fld)) {
+                               tree fieldtype;
+
+                               fieldtype = TREE_TYPE(fld);
+                               if (TREE_CODE(fieldtype) == INTEGER_TYPE)
+                                       continue;
+
+                               *no_add_attrs = true;
+                               error("structure variable %qD with %qE attribute has a non-integer field %qE",
+                                       *node, name, fld);
+                               break;
+                       }
+
+                       if (fld)
+                               break;
+
+#if BUILDING_GCC_VERSION <= 4007
+                       vals = VEC_alloc(constructor_elt, gc, nelt);
+#else
+                       vec_alloc(vals, nelt);
+#endif
+
+                       for (fld = lst; fld; fld = TREE_CHAIN(fld)) {
+                               tree random_const, fld_t = TREE_TYPE(fld);
+
+                               random_const = tree_get_random_const(fld_t);
+                               CONSTRUCTOR_APPEND_ELT(vals, fld, random_const);
+                       }
+
+                       /* Initialize the fields with random constants */
+                       DECL_INITIAL(*node) = build_constructor(type, vals);
+                       break;
+               }
+
+               /* Initialize the variable with a random constant */
+               case INTEGER_TYPE:
+                       DECL_INITIAL(*node) = tree_get_random_const(type);
+                       break;
+
+               case ARRAY_TYPE: {
+                       tree elt_type, array_size, elt_size;
+                       unsigned int i, nelt;
+                       HOST_WIDE_INT array_size_int, elt_size_int;
+
+                       elt_type = TREE_TYPE(type);
+                       elt_size = TYPE_SIZE_UNIT(TREE_TYPE(type));
+                       array_size = TYPE_SIZE_UNIT(type);
+
+                       if (TREE_CODE(elt_type) != INTEGER_TYPE || !array_size
+                               || TREE_CODE(array_size) != INTEGER_CST) {
+                               *no_add_attrs = true;
+                               error("array variable %qD with %qE attribute must be a fixed length integer array type",
+                                       *node, name);
+                               break;
+                       }
+
+                       array_size_int = TREE_INT_CST_LOW(array_size);
+                       elt_size_int = TREE_INT_CST_LOW(elt_size);
+                       nelt = array_size_int / elt_size_int;
+
+#if BUILDING_GCC_VERSION <= 4007
+                       vals = VEC_alloc(constructor_elt, gc, nelt);
+#else
+                       vec_alloc(vals, nelt);
+#endif
+
+                       for (i = 0; i < nelt; i++) {
+                               tree cst = size_int(i);
+                               tree rand_cst = tree_get_random_const(elt_type);
+
+                               CONSTRUCTOR_APPEND_ELT(vals, cst, rand_cst);
+                       }
+
+                       /*
+                        * Initialize the elements of the array with random
+                        * constants
+                        */
+                       DECL_INITIAL(*node) = build_constructor(type, vals);
+                       break;
+               }
+               }
+               break;
+
+       case FUNCTION_DECL:
+               break;
+       }
+
+       return NULL_TREE;
+}
+
+static struct attribute_spec latent_entropy_attr = {
+       .name                           = "latent_entropy",
+       .min_length                     = 0,
+       .max_length                     = 0,
+       .decl_required                  = true,
+       .type_required                  = false,
+       .function_type_required         = false,
+       .handler                        = handle_latent_entropy_attribute,
+#if BUILDING_GCC_VERSION >= 4007
+       .affects_type_identity          = false
+#endif
+};
+
+static void register_attributes(void *event_data __unused, void *data __unused)
+{
+       register_attribute(&latent_entropy_attr);
+}
+
+static bool latent_entropy_gate(void)
+{
+       tree list;
+
+       /* don't bother with noreturn functions for now */
+       if (TREE_THIS_VOLATILE(current_function_decl))
+               return false;
+
+       /* gcc-4.5 doesn't discover some trivial noreturn functions */
+       if (EDGE_COUNT(EXIT_BLOCK_PTR_FOR_FN(cfun)->preds) == 0)
+               return false;
+
+       list = DECL_ATTRIBUTES(current_function_decl);
+       return lookup_attribute("latent_entropy", list) != NULL_TREE;
+}
+
+static tree create_var(tree type, const char *name)
+{
+       tree var;
+
+       var = create_tmp_var(type, name);
+       add_referenced_var(var);
+       mark_sym_for_renaming(var);
+       return var;
+}
+
+/*
+ * Set up the next operation and its constant operand to use in the latent
+ * entropy PRNG. When RHS is specified, the request is for perturbing the
+ * local latent entropy variable, otherwise it is for perturbing the global
+ * latent entropy variable where the two operands are already given by the
+ * local and global latent entropy variables themselves.
+ *
+ * The operation is one of add/xor/rol when instrumenting the local entropy
+ * variable and one of add/xor when perturbing the global entropy variable.
+ * Rotation is not used for the latter case because it would transmit less
+ * entropy to the global variable than the other two operations.
+ */
+static enum tree_code get_op(tree *rhs)
+{
+       static enum tree_code op;
+       unsigned HOST_WIDE_INT random_const;
+
+       random_const = get_random_const();
+
+       switch (op) {
+       case BIT_XOR_EXPR:
+               op = PLUS_EXPR;
+               break;
+
+       case PLUS_EXPR:
+               if (rhs) {
+                       op = LROTATE_EXPR;
+                       /*
+                        * This code limits the value of random_const to
+                        * the size of a wide int for the rotation
+                        */
+                       random_const &= HOST_BITS_PER_WIDE_INT - 1;
+                       break;
+               }
+
+       case LROTATE_EXPR:
+       default:
+               op = BIT_XOR_EXPR;
+               break;
+       }
+       if (rhs)
+               *rhs = build_int_cstu(unsigned_intDI_type_node, random_const);
+       return op;
+}
+
+static gimple create_assign(enum tree_code code, tree lhs, tree op1,
+                               tree op2)
+{
+       return gimple_build_assign_with_ops(code, lhs, op1, op2);
+}
+
+static void perturb_local_entropy(basic_block bb, tree local_entropy)
+{
+       gimple_stmt_iterator gsi;
+       gimple assign;
+       tree rhs;
+       enum tree_code op;
+
+       op = get_op(&rhs);
+       assign = create_assign(op, local_entropy, local_entropy, rhs);
+       gsi = gsi_after_labels(bb);
+       gsi_insert_before(&gsi, assign, GSI_NEW_STMT);
+       update_stmt(assign);
+}
+
+static void __perturb_latent_entropy(gimple_stmt_iterator *gsi,
+                                       tree local_entropy)
+{
+       gimple assign;
+       tree temp;
+       enum tree_code op;
+
+       /* 1. create temporary copy of latent_entropy */
+       temp = create_var(unsigned_intDI_type_node, "tmp_latent_entropy");
+
+       /* 2. read... */
+       add_referenced_var(latent_entropy_decl);
+       mark_sym_for_renaming(latent_entropy_decl);
+       assign = gimple_build_assign(temp, latent_entropy_decl);
+       gsi_insert_before(gsi, assign, GSI_NEW_STMT);
+       update_stmt(assign);
+
+       /* 3. ...modify... */
+       op = get_op(NULL);
+       assign = create_assign(op, temp, temp, local_entropy);
+       gsi_insert_after(gsi, assign, GSI_NEW_STMT);
+       update_stmt(assign);
+
+       /* 4. ...write latent_entropy */
+       assign = gimple_build_assign(latent_entropy_decl, temp);
+       gsi_insert_after(gsi, assign, GSI_NEW_STMT);
+       update_stmt(assign);
+}
+
+static bool handle_tail_calls(basic_block bb, tree local_entropy)
+{
+       gimple_stmt_iterator gsi;
+
+       for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
+               gcall *call;
+               gimple stmt = gsi_stmt(gsi);
+
+               if (!is_gimple_call(stmt))
+                       continue;
+
+               call = as_a_gcall(stmt);
+               if (!gimple_call_tail_p(call))
+                       continue;
+
+               __perturb_latent_entropy(&gsi, local_entropy);
+               return true;
+       }
+
+       return false;
+}
+
+static void perturb_latent_entropy(tree local_entropy)
+{
+       edge_iterator ei;
+       edge e, last_bb_e;
+       basic_block last_bb;
+
+       gcc_assert(single_pred_p(EXIT_BLOCK_PTR_FOR_FN(cfun)));
+       last_bb_e = single_pred_edge(EXIT_BLOCK_PTR_FOR_FN(cfun));
+
+       FOR_EACH_EDGE(e, ei, last_bb_e->src->preds) {
+               if (ENTRY_BLOCK_PTR_FOR_FN(cfun) == e->src)
+                       continue;
+               if (EXIT_BLOCK_PTR_FOR_FN(cfun) == e->src)
+                       continue;
+
+               handle_tail_calls(e->src, local_entropy);
+       }
+
+       last_bb = single_pred(EXIT_BLOCK_PTR_FOR_FN(cfun));
+       if (!handle_tail_calls(last_bb, local_entropy)) {
+               gimple_stmt_iterator gsi = gsi_last_bb(last_bb);
+
+               __perturb_latent_entropy(&gsi, local_entropy);
+       }
+}
+
+static void init_local_entropy(basic_block bb, tree local_entropy)
+{
+       gimple assign, call;
+       tree frame_addr, rand_const, tmp, fndecl, udi_frame_addr;
+       enum tree_code op;
+       unsigned HOST_WIDE_INT rand_cst;
+       gimple_stmt_iterator gsi = gsi_after_labels(bb);
+
+       /* 1. create local_entropy_frameaddr */
+       frame_addr = create_var(ptr_type_node, "local_entropy_frameaddr");
+
+       /* 2. local_entropy_frameaddr = __builtin_frame_address() */
+       fndecl = builtin_decl_implicit(BUILT_IN_FRAME_ADDRESS);
+       call = gimple_build_call(fndecl, 1, integer_zero_node);
+       gimple_call_set_lhs(call, frame_addr);
+       gsi_insert_before(&gsi, call, GSI_NEW_STMT);
+       update_stmt(call);
+
+       udi_frame_addr = fold_convert(unsigned_intDI_type_node, frame_addr);
+       assign = gimple_build_assign(local_entropy, udi_frame_addr);
+       gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
+       update_stmt(assign);
+
+       /* 3. create temporary copy of latent_entropy */
+       tmp = create_var(unsigned_intDI_type_node, "tmp_latent_entropy");
+
+       /* 4. read the global entropy variable into local entropy */
+       add_referenced_var(latent_entropy_decl);
+       mark_sym_for_renaming(latent_entropy_decl);
+       assign = gimple_build_assign(tmp, latent_entropy_decl);
+       gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
+       update_stmt(assign);
+
+       /* 5. mix local_entropy_frameaddr into local entropy */
+       assign = create_assign(BIT_XOR_EXPR, local_entropy, local_entropy, tmp);
+       gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
+       update_stmt(assign);
+
+       rand_cst = get_random_const();
+       rand_const = build_int_cstu(unsigned_intDI_type_node, rand_cst);
+       op = get_op(NULL);
+       assign = create_assign(op, local_entropy, local_entropy, rand_const);
+       gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
+       update_stmt(assign);
+}
+
+static bool create_latent_entropy_decl(void)
+{
+       varpool_node_ptr node;
+
+       if (latent_entropy_decl != NULL_TREE)
+               return true;
+
+       FOR_EACH_VARIABLE(node) {
+               tree name, var = NODE_DECL(node);
+
+               if (DECL_NAME_LENGTH(var) < sizeof("latent_entropy") - 1)
+                       continue;
+
+               name = DECL_NAME(var);
+               if (strcmp(IDENTIFIER_POINTER(name), "latent_entropy"))
+                       continue;
+
+               latent_entropy_decl = var;
+               break;
+       }
+
+       return latent_entropy_decl != NULL_TREE;
+}
+
+static unsigned int latent_entropy_execute(void)
+{
+       basic_block bb;
+       tree local_entropy;
+
+       if (!create_latent_entropy_decl())
+               return 0;
+
+       /* prepare for step 2 below */
+       gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
+       bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
+       if (!single_pred_p(bb)) {
+               split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
+               gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
+               bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
+       }
+
+       /* 1. create the local entropy variable */
+       local_entropy = create_var(unsigned_intDI_type_node, "local_entropy");
+
+       /* 2. initialize the local entropy variable */
+       init_local_entropy(bb, local_entropy);
+
+       bb = bb->next_bb;
+
+       /*
+        * 3. instrument each BB with an operation on the
+        *    local entropy variable
+        */
+       while (bb != EXIT_BLOCK_PTR_FOR_FN(cfun)) {
+               perturb_local_entropy(bb, local_entropy);
+               bb = bb->next_bb;
+       };
+
+       /* 4. mix local entropy into the global entropy variable */
+       perturb_latent_entropy(local_entropy);
+       return 0;
+}
+
+static void latent_entropy_start_unit(void *gcc_data __unused,
+                                       void *user_data __unused)
+{
+       tree type, id;
+       int quals;
+
+       seed = get_random_seed(false);
+
+       if (in_lto_p)
+               return;
+
+       /* extern volatile u64 latent_entropy */
+       gcc_assert(TYPE_PRECISION(long_long_unsigned_type_node) == 64);
+       quals = TYPE_QUALS(long_long_unsigned_type_node) | TYPE_QUAL_VOLATILE;
+       type = build_qualified_type(long_long_unsigned_type_node, quals);
+       id = get_identifier("latent_entropy");
+       latent_entropy_decl = build_decl(UNKNOWN_LOCATION, VAR_DECL, id, type);
+
+       TREE_STATIC(latent_entropy_decl) = 1;
+       TREE_PUBLIC(latent_entropy_decl) = 1;
+       TREE_USED(latent_entropy_decl) = 1;
+       DECL_PRESERVE_P(latent_entropy_decl) = 1;
+       TREE_THIS_VOLATILE(latent_entropy_decl) = 1;
+       DECL_EXTERNAL(latent_entropy_decl) = 1;
+       DECL_ARTIFICIAL(latent_entropy_decl) = 1;
+       lang_hooks.decls.pushdecl(latent_entropy_decl);
+}
+
+#define PASS_NAME latent_entropy
+#define PROPERTIES_REQUIRED PROP_gimple_leh | PROP_cfg
+#define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func \
+       | TODO_update_ssa
+#include "gcc-generate-gimple-pass.h"
+
+int plugin_init(struct plugin_name_args *plugin_info,
+               struct plugin_gcc_version *version)
+{
+       bool enabled = true;
+       const char * const plugin_name = plugin_info->base_name;
+       const int argc = plugin_info->argc;
+       const struct plugin_argument * const argv = plugin_info->argv;
+       int i;
+
+       struct register_pass_info latent_entropy_pass_info;
+
+       latent_entropy_pass_info.pass           = make_latent_entropy_pass();
+       latent_entropy_pass_info.reference_pass_name            = "optimized";
+       latent_entropy_pass_info.ref_pass_instance_number       = 1;
+       latent_entropy_pass_info.pos_op         = PASS_POS_INSERT_BEFORE;
+       static const struct ggc_root_tab gt_ggc_r_gt_latent_entropy[] = {
+               {
+                       .base = &latent_entropy_decl,
+                       .nelt = 1,
+                       .stride = sizeof(latent_entropy_decl),
+                       .cb = &gt_ggc_mx_tree_node,
+                       .pchw = &gt_pch_nx_tree_node
+               },
+               LAST_GGC_ROOT_TAB
+       };
+
+       if (!plugin_default_version_check(version, &gcc_version)) {
+               error(G_("incompatible gcc/plugin versions"));
+               return 1;
+       }
+
+       for (i = 0; i < argc; ++i) {
+               if (!(strcmp(argv[i].key, "disable"))) {
+                       enabled = false;
+                       continue;
+               }
+               error(G_("unkown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
+       }
+
+       register_callback(plugin_name, PLUGIN_INFO, NULL,
+                               &latent_entropy_plugin_info);
+       if (enabled) {
+               register_callback(plugin_name, PLUGIN_START_UNIT,
+                                       &latent_entropy_start_unit, NULL);
+               register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS,
+                                 NULL, (void *)&gt_ggc_r_gt_latent_entropy);
+               register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+                                       &latent_entropy_pass_info);
+       }
+       register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes,
+                               NULL);
+
+       return 0;
+}
index 17fa901418ae6a491ba61f3814a1143e2a62bfde..0055b07b03b68cafd0bc3c400ee23b686632efbf 100755 (executable)
@@ -97,7 +97,10 @@ print_mtime() {
 }
 
 list_parse() {
-       [ ! -L "$1" ] && echo "$1 \\" || :
+       if [ -L "$1" ]; then
+               return
+       fi
+       echo "$1" | sed 's/:/\\:/g; s/$/ \\/'
 }
 
 # for each file print a line in following format
index e583565f2011dae4aedc8ae2e0c83fa286687058..5235aa507ba533cbd631f2ba81f702d97b50925c 100644 (file)
@@ -289,6 +289,23 @@ repeat:
        }
       break;
 
+    case ST_TYPEOF_1:
+      if (token == IDENT)
+       {
+         if (is_reserved_word(yytext, yyleng)
+             || find_symbol(yytext, SYM_TYPEDEF, 1))
+           {
+             yyless(0);
+             unput('(');
+             lexstate = ST_NORMAL;
+             token = TYPEOF_KEYW;
+             break;
+           }
+         _APP("(", 1);
+       }
+       lexstate = ST_TYPEOF;
+       /* FALLTHRU */
+
     case ST_TYPEOF:
       switch (token)
        {
@@ -313,24 +330,6 @@ repeat:
        }
       break;
 
-    case ST_TYPEOF_1:
-      if (token == IDENT)
-       {
-         if (is_reserved_word(yytext, yyleng)
-             || find_symbol(yytext, SYM_TYPEDEF, 1))
-           {
-             yyless(0);
-             unput('(');
-             lexstate = ST_NORMAL;
-             token = TYPEOF_KEYW;
-             break;
-           }
-         _APP("(", 1);
-       }
-       APP;
-       lexstate = ST_TYPEOF;
-       goto repeat;
-
     case ST_BRACKET:
       APP;
       switch (token)
index f82740a69b850ce7f496700a42ef8c75f53e2f80..985c5541aae411add700c6e1158b9ebc9dca046f 100644 (file)
@@ -2098,6 +2098,23 @@ repeat:
        }
       break;
 
+    case ST_TYPEOF_1:
+      if (token == IDENT)
+       {
+         if (is_reserved_word(yytext, yyleng)
+             || find_symbol(yytext, SYM_TYPEDEF, 1))
+           {
+             yyless(0);
+             unput('(');
+             lexstate = ST_NORMAL;
+             token = TYPEOF_KEYW;
+             break;
+           }
+         _APP("(", 1);
+       }
+       lexstate = ST_TYPEOF;
+       /* FALLTHRU */
+
     case ST_TYPEOF:
       switch (token)
        {
@@ -2122,24 +2139,6 @@ repeat:
        }
       break;
 
-    case ST_TYPEOF_1:
-      if (token == IDENT)
-       {
-         if (is_reserved_word(yytext, yyleng)
-             || find_symbol(yytext, SYM_TYPEDEF, 1))
-           {
-             yyless(0);
-             unput('(');
-             lexstate = ST_NORMAL;
-             token = TYPEOF_KEYW;
-             break;
-           }
-         _APP("(", 1);
-       }
-       APP;
-       lexstate = ST_TYPEOF;
-       goto repeat;
-
     case ST_BRACKET:
       APP;
       switch (token)
index 4f727eb5ec43f294e0a508244bab4249c9001575..f742c65108b9d41f0c4d0bbed22924fc35383b37 100755 (executable)
@@ -37,12 +37,40 @@ info()
        fi
 }
 
+# Thin archive build here makes a final archive with
+# symbol table and indexes from vmlinux objects, which can be
+# used as input to linker.
+#
+# Traditional incremental style of link does not require this step
+#
+# built-in.o output file
+#
+archive_builtin()
+{
+       if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+               info AR built-in.o
+               rm -f built-in.o;
+               ${AR} rcsT${KBUILD_ARFLAGS} built-in.o                  \
+                                       ${KBUILD_VMLINUX_INIT}          \
+                                       ${KBUILD_VMLINUX_MAIN}
+       fi
+}
+
 # Link of vmlinux.o used for section mismatch analysis
 # ${1} output file
 modpost_link()
 {
-       ${LD} ${LDFLAGS} -r -o ${1} ${KBUILD_VMLINUX_INIT}                   \
-               --start-group ${KBUILD_VMLINUX_MAIN} --end-group
+       local objects
+
+       if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+               objects="--whole-archive built-in.o"
+       else
+               objects="${KBUILD_VMLINUX_INIT}                         \
+                       --start-group                                   \
+                       ${KBUILD_VMLINUX_MAIN}                          \
+                       --end-group"
+       fi
+       ${LD} ${LDFLAGS} -r -o ${1} ${objects}
 }
 
 # Link of vmlinux
@@ -51,18 +79,36 @@ modpost_link()
 vmlinux_link()
 {
        local lds="${objtree}/${KBUILD_LDS}"
+       local objects
 
        if [ "${SRCARCH}" != "um" ]; then
-               ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2}                  \
-                       -T ${lds} ${KBUILD_VMLINUX_INIT}                     \
-                       --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
+               if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+                       objects="--whole-archive built-in.o ${1}"
+               else
+                       objects="${KBUILD_VMLINUX_INIT}                 \
+                               --start-group                           \
+                               ${KBUILD_VMLINUX_MAIN}                  \
+                               --end-group                             \
+                               ${1}"
+               fi
+
+               ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2}             \
+                       -T ${lds} ${objects}
        else
-               ${CC} ${CFLAGS_vmlinux} -o ${2}                              \
-                       -Wl,-T,${lds} ${KBUILD_VMLINUX_INIT}                 \
-                       -Wl,--start-group                                    \
-                                ${KBUILD_VMLINUX_MAIN}                      \
-                       -Wl,--end-group                                      \
-                       -lutil -lrt -lpthread ${1}
+               if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+                       objects="-Wl,--whole-archive built-in.o ${1}"
+               else
+                       objects="${KBUILD_VMLINUX_INIT}                 \
+                               -Wl,--start-group                       \
+                               ${KBUILD_VMLINUX_MAIN}                  \
+                               -Wl,--end-group                         \
+                               ${1}"
+               fi
+
+               ${CC} ${CFLAGS_vmlinux} -o ${2}                         \
+                       -Wl,-T,${lds}                                   \
+                       ${objects}                                      \
+                       -lutil -lrt -lpthread
                rm -f linux
        fi
 }
@@ -119,6 +165,7 @@ cleanup()
        rm -f .tmp_kallsyms*
        rm -f .tmp_version
        rm -f .tmp_vmlinux*
+       rm -f built-in.o
        rm -f System.map
        rm -f vmlinux
        rm -f vmlinux.o
@@ -162,6 +209,8 @@ case "${KCONFIG_CONFIG}" in
        . "./${KCONFIG_CONFIG}"
 esac
 
+archive_builtin
+
 #link vmlinux.o
 info LD vmlinux.o
 modpost_link vmlinux.o
index ade7c6cad172a13833a3b41799a142ebf4cb4f46..682b73af77661a4c6260b9f450e015d86c453ede 100644 (file)
@@ -881,7 +881,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
         * the execve().
         */
        if (get_user_pages_remote(current, bprm->mm, pos, 1,
-                               0, 1, &page, NULL) <= 0)
+                               FOLL_FORCE, &page, NULL) <= 0)
                return false;
 #else
        page = bprm->page[pos / PAGE_SIZE];
index fce5697e42614a36056079b2a1aca289aec2d850..8c35072166769b9ea775a91c0a36b5c6ceacf866 100644 (file)
@@ -58,7 +58,7 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
                goto error;
        data->kernel = NULL;
 
-       err = snd_seq_kernel_client_ctl(client->number, cmd, &data);
+       err = snd_seq_kernel_client_ctl(client->number, cmd, data);
        if (err < 0)
                goto error;
 
index 9c22f95838efbd31d23448cc26321918c03bd5e1..19d41da79f93cc95500859c9e1690a2d89935d23 100644 (file)
@@ -49,7 +49,7 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec,
                removefunc = true;
                if (dell_led_set_func(DELL_LED_MICMUTE, false) >= 0) {
                        dell_led_value = 0;
-                       if (spec->gen.num_adc_nids > 1)
+                       if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch)
                                codec_dbg(codec, "Skipping micmute LED control due to several ADCs");
                        else {
                                dell_old_cap_hook = spec->gen.cap_sync_hook;
index f0955fd7a2e73157a661ad629a8099d02abfd1bf..6a23302297c9cc4b74fd66eab52a32433a45949c 100644 (file)
@@ -62,7 +62,7 @@ static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
                        removefunc = false;
                }
                if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) {
-                       if (spec->num_adc_nids > 1)
+                       if (spec->num_adc_nids > 1 && !spec->dyn_adc_switch)
                                codec_dbg(codec,
                                          "Skipping micmute LED control due to several ADCs");
                        else {
index 14e587e706554b4e07fc4eb42dfd55a14808a572..90009c0b3a92e42f2598e05b451a0cbc0d79c8bf 100644 (file)
@@ -604,8 +604,8 @@ line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
        }
 
        data_copy = memdup_user(data, count);
-       if (IS_ERR(ERR_PTR))
-               return -ENOMEM;
+       if (IS_ERR(data_copy))
+               return PTR_ERR(data_copy);
 
        rv = line6_send_raw_message(line6, data_copy, count);
 
index 9352a44ae6e4a5dcb4230c7192ec75f9a788e5d1..49cd4a65e390c8b56a0d5986e3d561fe407c6d80 100644 (file)
@@ -317,7 +317,8 @@ static int podhd_init(struct usb_line6 *line6,
        if (pod->line6.properties->capabilities & LINE6_CAP_PCM) {
                /* initialize PCM subsystem: */
                err = line6_init_pcm(line6,
-                       (id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
+                       (id->driver_info == LINE6_PODX3 ||
+                       id->driver_info == LINE6_PODX3LIVE) ? &podx3_pcm_properties :
                        &podhd_pcm_properties);
                if (err < 0)
                        return err;
diff --git a/tools/accounting/Makefile b/tools/accounting/Makefile
new file mode 100644 (file)
index 0000000..647c94a
--- /dev/null
@@ -0,0 +1,9 @@
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -I../../usr/include
+
+PROGS := getdelays
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
diff --git a/tools/laptop/dslm/Makefile b/tools/laptop/dslm/Makefile
new file mode 100644 (file)
index 0000000..ff613b3
--- /dev/null
@@ -0,0 +1,9 @@
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -I../../usr/include
+
+PROGS := dslm
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
index c0c0b265e88e54b868858a812839c4c24979651e..b63a31be1218830eecc6116cf301f375c0a046c1 100644 (file)
@@ -98,6 +98,15 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                        *type = INSN_FP_SETUP;
                break;
 
+       case 0x8d:
+               if (insn.rex_prefix.bytes &&
+                   insn.rex_prefix.bytes[0] == 0x48 &&
+                   insn.modrm.nbytes && insn.modrm.bytes[0] == 0x2c &&
+                   insn.sib.nbytes && insn.sib.bytes[0] == 0x24)
+                       /* lea %(rsp), %rbp */
+                       *type = INSN_FP_SETUP;
+               break;
+
        case 0x90:
                *type = INSN_NOP;
                break;
index 143b6cdd7f068f88fa8487aba804621c37523211..4490601a9235472bf48b4680a5bc5eac11ebcb30 100644 (file)
@@ -97,6 +97,19 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file,
        return next;
 }
 
+static bool gcov_enabled(struct objtool_file *file)
+{
+       struct section *sec;
+       struct symbol *sym;
+
+       list_for_each_entry(sec, &file->elf->sections, list)
+               list_for_each_entry(sym, &sec->symbol_list, list)
+                       if (!strncmp(sym->name, "__gcov_.", 8))
+                               return true;
+
+       return false;
+}
+
 #define for_each_insn(file, insn)                                      \
        list_for_each_entry(insn, &file->insn_list, list)
 
@@ -713,6 +726,7 @@ static struct rela *find_switch_table(struct objtool_file *file,
                                      struct instruction *insn)
 {
        struct rela *text_rela, *rodata_rela;
+       struct instruction *orig_insn = insn;
 
        text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
        if (text_rela && text_rela->sym == file->rodata->sym) {
@@ -733,10 +747,16 @@ static struct rela *find_switch_table(struct objtool_file *file,
 
        /* case 3 */
        func_for_each_insn_continue_reverse(file, func, insn) {
-               if (insn->type == INSN_JUMP_UNCONDITIONAL ||
-                   insn->type == INSN_JUMP_DYNAMIC)
+               if (insn->type == INSN_JUMP_DYNAMIC)
                        break;
 
+               /* allow small jumps within the range */
+               if (insn->type == INSN_JUMP_UNCONDITIONAL &&
+                   insn->jump_dest &&
+                   (insn->jump_dest->offset <= insn->offset ||
+                    insn->jump_dest->offset >= orig_insn->offset))
+                   break;
+
                text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
                                                    insn->len);
                if (text_rela && text_rela->sym == file->rodata->sym)
@@ -1034,34 +1054,6 @@ static int validate_branch(struct objtool_file *file,
        return 0;
 }
 
-static bool is_gcov_insn(struct instruction *insn)
-{
-       struct rela *rela;
-       struct section *sec;
-       struct symbol *sym;
-       unsigned long offset;
-
-       rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
-       if (!rela)
-               return false;
-
-       if (rela->sym->type != STT_SECTION)
-               return false;
-
-       sec = rela->sym->sec;
-       offset = rela->addend + insn->offset + insn->len - rela->offset;
-
-       list_for_each_entry(sym, &sec->symbol_list, list) {
-               if (sym->type != STT_OBJECT)
-                       continue;
-
-               if (offset >= sym->offset && offset < sym->offset + sym->len)
-                       return (!memcmp(sym->name, "__gcov0.", 8));
-       }
-
-       return false;
-}
-
 static bool is_kasan_insn(struct instruction *insn)
 {
        return (insn->type == INSN_CALL &&
@@ -1083,9 +1075,6 @@ static bool ignore_unreachable_insn(struct symbol *func,
        if (insn->type == INSN_NOP)
                return true;
 
-       if (is_gcov_insn(insn))
-               return true;
-
        /*
         * Check if this (or a subsequent) instruction is related to
         * CONFIG_UBSAN or CONFIG_KASAN.
@@ -1146,6 +1135,19 @@ static int validate_functions(struct objtool_file *file)
                                    ignore_unreachable_insn(func, insn))
                                        continue;
 
+                               /*
+                                * gcov produces a lot of unreachable
+                                * instructions.  If we get an unreachable
+                                * warning and the file has gcov enabled, just
+                                * ignore it, and all other such warnings for
+                                * the file.
+                                */
+                               if (!file->ignore_unreachables &&
+                                   gcov_enabled(file)) {
+                                       file->ignore_unreachables = true;
+                                       continue;
+                               }
+
                                WARN_FUNC("function has unreachable instruction", insn->sec, insn->offset);
                                warnings++;
                        }
diff --git a/tools/pcmcia/Makefile b/tools/pcmcia/Makefile
new file mode 100644 (file)
index 0000000..81a7498
--- /dev/null
@@ -0,0 +1,9 @@
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -I../../usr/include
+
+PROGS := crc32hash
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
index 5ce61a1bda9ca863cbcd4c327f2abd8f621bf1f6..df14e6b67b63b781846d71b80cbc6e8ee1edc0e3 100644 (file)
@@ -36,7 +36,7 @@ SOLIBEXT=so
 # The following works at least on fedora 23, you may need the next
 # line for other distros.
 ifneq (,$(wildcard /usr/sbin/update-java-alternatives))
-JDIR=$(shell /usr/sbin/update-java-alternatives -l | head -1 | cut -d ' ' -f 3)
+JDIR=$(shell /usr/sbin/update-java-alternatives -l | head -1 | awk '{print $$3}')
 else
   ifneq (,$(wildcard /usr/sbin/alternatives))
     JDIR=$(shell alternatives --display java | tail -1 | cut -d' ' -f 5 | sed 's%/jre/bin/java.%%g')
index fb8e42c7507add43bc9c1848cf3ef300382ee313..4ffff7be92993e02f37fd4be8d79c23b11be9d85 100644 (file)
@@ -601,7 +601,8 @@ int hist_browser__run(struct hist_browser *browser, const char *help)
                        u64 nr_entries;
                        hbt->timer(hbt->arg);
 
-                       if (hist_browser__has_filter(browser))
+                       if (hist_browser__has_filter(browser) ||
+                           symbol_conf.report_hierarchy)
                                hist_browser__update_nr_entries(browser);
 
                        nr_entries = hist_browser__nr_entries(browser);
index 85dd0db0a127995a725eade74a12e5da8569b80c..2f3eded54b0cc65a6d2ac59456a2acf2d7921059 100644 (file)
@@ -1895,7 +1895,6 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse
        if (ph->needs_swap)
                nr = bswap_32(nr);
 
-       ph->env.nr_numa_nodes = nr;
        nodes = zalloc(sizeof(*nodes) * nr);
        if (!nodes)
                return -ENOMEM;
@@ -1932,6 +1931,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse
 
                free(str);
        }
+       ph->env.nr_numa_nodes = nr;
        ph->env.numa_nodes = nodes;
        return 0;
 
index 9f43fda2570f959833c85b89aa29b3612c6d6abb..660fca05bc93bd8f724a9f3cef3f0f3d86abb983 100644 (file)
@@ -136,8 +136,8 @@ do {                                                        \
 group          [^,{}/]*[{][^}]*[}][^,{}/]*
 event_pmu      [^,{}/]+[/][^/]*[/][^,{}/]*
 event          [^,{}/]+
-bpf_object     .*\.(o|bpf)
-bpf_source     .*\.c
+bpf_object     [^,{}]+\.(o|bpf)
+bpf_source     [^,{}]+\.c
 
 num_dec                [0-9]+
 num_hex                0x[a-fA-F0-9]+
diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile
new file mode 100644 (file)
index 0000000..0ab1130
--- /dev/null
@@ -0,0 +1,7 @@
+TEST_PROGS := dnotify_test
+all: $(TEST_PROGS)
+
+include ../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
index e87dbe2a0b0d2b4f09b73e37192ffff53ad3a5ae..7ff002eed62473572931bf7b3b28748dad9ebdd3 100755 (executable)
@@ -24,7 +24,7 @@
 
 # Test for a color capable console
 if [ -z "$USE_COLOR" ]; then
-    tput setf 7
+    tput setf 7 || tput setaf 7
     if [ $? -eq 0 ]; then
         USE_COLOR=1
         tput sgr0
index 4126312ad64e6080887f6c9d756c5ea1c9f6a145..88bcb1767362dcb38b9e0c62131d89362ba2ba86 100755 (executable)
@@ -23,7 +23,7 @@
 
 # Test for a color capable shell and pass the result to the subdir scripts
 USE_COLOR=0
-tput setf 7
+tput setf 7 || tput setaf 7
 if [ $? -eq 0 ]; then
     USE_COLOR=1
     tput sgr0
diff --git a/tools/testing/selftests/ia64/Makefile b/tools/testing/selftests/ia64/Makefile
new file mode 100644 (file)
index 0000000..2b3de2d
--- /dev/null
@@ -0,0 +1,8 @@
+TEST_PROGS := aliasing-test
+
+all: $(TEST_PROGS)
+
+include ../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
diff --git a/tools/testing/selftests/networking/timestamping/Makefile b/tools/testing/selftests/networking/timestamping/Makefile
new file mode 100644 (file)
index 0000000..ccbb9ed
--- /dev/null
@@ -0,0 +1,8 @@
+TEST_PROGS := hwtstamp_config timestamping txtimestamp
+
+all: $(TEST_PROGS)
+
+include ../../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
diff --git a/tools/testing/selftests/powerpc/copyloops/asm/export.h b/tools/testing/selftests/powerpc/copyloops/asm/export.h
new file mode 100644 (file)
index 0000000..2d14a9b
--- /dev/null
@@ -0,0 +1 @@
+#define EXPORT_SYMBOL(x)
index 4fe13a439fd7f22be735a3f084a28a94ab90e104..50ded63e25b710deadc13467cf7849c9f329ff6f 100644 (file)
@@ -4,3 +4,4 @@ fpu_preempt
 vmx_preempt
 fpu_signal
 vmx_signal
+vsx_preempt
diff --git a/tools/testing/selftests/powerpc/signal/.gitignore b/tools/testing/selftests/powerpc/signal/.gitignore
new file mode 100644 (file)
index 0000000..1b89224
--- /dev/null
@@ -0,0 +1,2 @@
+signal
+signal_tm
diff --git a/tools/testing/selftests/powerpc/stringloops/asm/export.h b/tools/testing/selftests/powerpc/stringloops/asm/export.h
new file mode 100644 (file)
index 0000000..2d14a9b
--- /dev/null
@@ -0,0 +1 @@
+#define EXPORT_SYMBOL(x)
index 82c0a9ce6e748129ffc3794be52b913626d66814..427621792229e68c7fcdaff89f28782bda14c1da 100644 (file)
@@ -7,3 +7,7 @@ tm-fork
 tm-tar
 tm-tmspr
 tm-exec
+tm-signal-context-chk-fpu
+tm-signal-context-chk-gpr
+tm-signal-context-chk-vmx
+tm-signal-context-chk-vsx
diff --git a/tools/testing/selftests/prctl/Makefile b/tools/testing/selftests/prctl/Makefile
new file mode 100644 (file)
index 0000000..35aa1c8
--- /dev/null
@@ -0,0 +1,15 @@
+ifndef CROSS_COMPILE
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
+
+ifeq ($(ARCH),x86)
+TEST_PROGS := disable-tsc-ctxt-sw-stress-test disable-tsc-on-off-stress-test \
+               disable-tsc-test
+all: $(TEST_PROGS)
+
+include ../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
+endif
+endif
diff --git a/tools/testing/selftests/ptp/Makefile b/tools/testing/selftests/ptp/Makefile
new file mode 100644 (file)
index 0000000..83dd42b
--- /dev/null
@@ -0,0 +1,8 @@
+TEST_PROGS := testptp
+LDLIBS += -lrt
+all: $(TEST_PROGS)
+
+include ../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
index 5a246a02dff3c6986a1bd06496473ec5be1fd252..15cf56d32155328d7ea064988b2539f4d6e2157a 100644 (file)
@@ -122,7 +122,7 @@ static int check_itimer(int which)
        else if (which == ITIMER_REAL)
                idle_loop();
 
-       gettimeofday(&end, NULL);
+       err = gettimeofday(&end, NULL);
        if (err < 0) {
                perror("Can't call gettimeofday()\n");
                return -1;
@@ -175,7 +175,7 @@ static int check_timer_create(int which)
 
        user_loop();
 
-       gettimeofday(&end, NULL);
+       err = gettimeofday(&end, NULL);
        if (err < 0) {
                perror("Can't call gettimeofday()\n");
                return -1;
diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile
new file mode 100644 (file)
index 0000000..706b68b
--- /dev/null
@@ -0,0 +1,20 @@
+ifndef CROSS_COMPILE
+CFLAGS := -std=gnu99
+CFLAGS_vdso_standalone_test_x86 := -nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector
+ifeq ($(CONFIG_X86_32),y)
+LDLIBS += -lgcc_s
+endif
+
+TEST_PROGS := vdso_test vdso_standalone_test_x86
+
+all: $(TEST_PROGS)
+vdso_test: parse_vdso.c vdso_test.c
+vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c
+       $(CC) $(CFLAGS) $(CFLAGS_vdso_standalone_test_x86) \
+               vdso_standalone_test_x86.c parse_vdso.c \
+               -o vdso_standalone_test_x86
+
+include ../lib.mk
+clean:
+       rm -fr $(TEST_PROGS)
+endif
diff --git a/tools/testing/selftests/watchdog/.gitignore b/tools/testing/selftests/watchdog/.gitignore
new file mode 100644 (file)
index 0000000..5aac515
--- /dev/null
@@ -0,0 +1 @@
+watchdog-test
diff --git a/tools/testing/selftests/watchdog/Makefile b/tools/testing/selftests/watchdog/Makefile
new file mode 100644 (file)
index 0000000..f863c66
--- /dev/null
@@ -0,0 +1,8 @@
+TEST_PROGS := watchdog-test
+
+all: $(TEST_PROGS)
+
+include ../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
index eb17917c8a3a57b2551abee6d4fa873e384955b5..7972cc5124080b5566a6aa151db6f4169997533b 100644 (file)
@@ -13,7 +13,7 @@ Statistics for individual zram devices are exported through sysfs nodes at
 
 Kconfig required:
 CONFIG_ZRAM=y
-CONFIG_ZRAM_LZ4_COMPRESS=y
+CONFIG_CRYPTO_LZ4=y
 CONFIG_ZPOOL=y
 CONFIG_ZSMALLOC=y
 
index db9668869f6ff6866a72f278def5770d71190994..8035cc1eb9551ab58b2f454f9ff74546dd2ebdde 100644 (file)
@@ -84,7 +84,8 @@ static void async_pf_execute(struct work_struct *work)
         * mm and might be done in another context, so we must
         * use FOLL_REMOTE.
         */
-       __get_user_pages_unlocked(NULL, mm, addr, 1, 1, 0, NULL, FOLL_REMOTE);
+       __get_user_pages_unlocked(NULL, mm, addr, 1, NULL,
+                       FOLL_WRITE | FOLL_REMOTE);
 
        kvm_async_page_present_sync(vcpu, apf);
 
index 81dfc73d3df39851e3b44cd546f5f66046ec5e0f..28510e72618a00fed5ef83d0e588ef5e08dc5b84 100644 (file)
@@ -1416,10 +1416,15 @@ static int hva_to_pfn_slow(unsigned long addr, bool *async, bool write_fault,
                down_read(&current->mm->mmap_sem);
                npages = get_user_page_nowait(addr, write_fault, page);
                up_read(&current->mm->mmap_sem);
-       } else
+       } else {
+               unsigned int flags = FOLL_TOUCH | FOLL_HWPOISON;
+
+               if (write_fault)
+                       flags |= FOLL_WRITE;
+
                npages = __get_user_pages_unlocked(current, current->mm, addr, 1,
-                                                  write_fault, 0, page,
-                                                  FOLL_TOUCH|FOLL_HWPOISON);
+                                                  page, flags);
+       }
        if (npages != 1)
                return npages;